Skip to content

Commit c9b9a3e

Browse files
Add support to easily run any JMX with the DSL
Additionally, use config jmeter dependency to avoid copying required properties file to resources folder and keep project cleaner.
1 parent b0485c0 commit c9b9a3e

File tree

9 files changed

+160
-463
lines changed

9 files changed

+160
-463
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
name: run tests
2-
on:
3-
push:
4-
branches: '*'
2+
on: push
53
jobs:
64
test:
75
runs-on: ubuntu-latest

README.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Here is a simple example test in [JUnit 5](https://junit.org/junit5/)+ with 2 th
2020
import static org.assertj.core.api.Assertions.assertThat;
2121
import static us.abstracta.jmeter.javadsl.JmeterDsl.*;
2222

23+
import java.io.IOException;
2324
import java.time.Duration;
2425
import org.eclipse.jetty.http.MimeTypes.Type;
2526
import org.junit.jupiter.api.Test;
@@ -57,7 +58,7 @@ There are many tools to script performance/load tests, being [JMeter] and [Gatli
5758

5859
JMeter is great for people with no programming knowledge since it provides a graphical interface to create test plans and run them. Additionally, it is the most popular tool (with a lot of supporting tools built on it) and has a big amount of supported protocols and plugins that makes it very versatile.
5960

60-
But, JMeter has some problems as well: sometimes might be slow to create test plans in JMeter GUI, and you can't get the full picture of the test plan unless you dig in every tree node to check its properties. Furthermore, it doesn't provide a simple programmer friendly API (you can check [here](https://www.blazemeter.com/blog/5-ways-launch-jmeter-test-without-using-jmeter-gui/) for an example on how to run jmeter programmatically without jmeter-java-dsl), nor a VCS friendly format (too verbose and hard to review). For example, for the same test plan previously showed with jmeter-java-dsl, in JMeter you would need a JMX file like [this](docs/sample.jmx), and even then, it wouldn't be as simple to do assertions on collected statistics as in provided example.
61+
But, JMeter has some problems as well: sometimes might be slow to create test plans in JMeter GUI, and you can't get the full picture of the test plan unless you dig in every tree node to check its properties. Furthermore, it doesn't provide a simple programmer friendly API (you can check [here](https://www.blazemeter.com/blog/5-ways-launch-jmeter-test-without-using-jmeter-gui/) for an example on how to run JMeter programmatically without jmeter-java-dsl), nor a VCS friendly format (too verbose and hard to review). For example, for the same test plan previously showed with jmeter-java-dsl, in JMeter you would need a JMX file like [this](docs/sample.jmx), and even then, it wouldn't be as simple to do assertions on collected statistics as in provided example.
6162

6263
Gatling does provide a simple API and a VCS friendly format, but requires scala knowledge and environment. Additionally, it doesn't provide as rich environment as JMeter (protocol support, plugins, tools) and requires learning a new framework for testing (if you already use JMeter, which is the most popular tool).
6364

@@ -166,7 +167,59 @@ public class SaveTestPlanAsJMX {
166167
167168
> Take into consideration that currently there is no automatic way to migrate changes done in JMX to the Java DSL.
168169
169-
This can be helpful to share a Java DSL defined test plan with people not used to the DSL, or to use some JMeter feature (or plugin) that is not yet supported by the DSL (**but, we strongly encourage you to report it as an issue**, so we can implement support for it).
170+
This can be helpful to share a Java DSL defined test plan with people not used to the DSL, or to use some JMeter feature (or plugin) that is not yet supported by the DSL (**but, we strongly encourage you to report it as an issue**, so we can implement support for it).
171+
172+
### Run JMX file
173+
174+
jmeter-java-dsl also provides means to easily run a test plan from a JMX file either locally or in BlazeMeter (through previously mentioned jmeter-java-dsl-blazemeter module). Here is an example:
175+
176+
```java
177+
import static org.assertj.core.api.Assertions.assertThat;
178+
179+
import java.io.IOException;
180+
import java.time.Duration;
181+
import org.junit.jupiter.api.Test;
182+
import us.abstracta.jmeter.javadsl.core.DslTestPlan;
183+
import us.abstracta.jmeter.javadsl.core.TestPlanStats;
184+
185+
public class RunJmxTestPlan {
186+
187+
@Test
188+
public void testPerformance() throws IOException {
189+
TestPlanStats stats = DslTestPlan.fromJmx("test-plan.jmx").run();
190+
assertThat(stats.overall().elapsedTimePercentile99()).isLessThan(Duration.ofSeconds(5));
191+
}
192+
193+
}
194+
```
195+
196+
This can be used to just run existing JMX files, or when DSL has no support for some JMeter functionality or plugin and you need to use JMeter GUI to build the test plan but still want to use jmeter-java-dsl to run the test plan embedded in Java test or code.
197+
198+
> When the JMX uses some custom plugins or JMeter protocol support, you might need to add required dependencies to be able to run the test in an embedded engine. For example, when running a TN3270 JMX test plan using RTE plugin you will need to add following repository and dependencies:
199+
> ```xml
200+
> <repositories>
201+
> <repository>
202+
> <id>jitpack.io</id>
203+
> <url>https://jitpack.io</url>
204+
> </repository>
205+
> </repositories>
206+
>
207+
> <dependencies>
208+
> ...
209+
> <dependency>
210+
> <groupId>com.github.Blazemeter</groupId>
211+
> <artifactId>RTEPlugin</artifactId>
212+
> <version>3.1</version>
213+
> <scope>test</scope>
214+
> </dependency>
215+
> <dependency>
216+
> <groupId>com.github.Blazemeter</groupId>
217+
> <artifactId>dm3270</artifactId>
218+
> <version>0.12.3-lib</version>
219+
> <scope>test</scope>
220+
> </dependency>
221+
> </dependencies>
222+
> ```
170223
171224
## Contributing & Requesting features
172225

docs/classes.puml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ package core {
3333
TestPlanStats run()
3434
TestPlanStats runIn(DslJmeterEngine engine)
3535
void saveAsJmx(String filePath)
36+
{static} DslTestPlan fromJmx(String filePath)
3637
}
3738

3839
interface TestPlanChild extends DslTestElement {

jmeter-java-dsl/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,21 @@
3232
<artifactId>ApacheJMeter_functions</artifactId>
3333
<version>${jmeter.version}</version>
3434
</dependency>
35+
<dependency>
36+
<groupId>org.apache.jmeter</groupId>
37+
<artifactId>ApacheJMeter_config</artifactId>
38+
<version>${jmeter.version}</version>
39+
</dependency>
3540
<dependency>
3641
<groupId>org.eclipse.jetty</groupId>
3742
<artifactId>jetty-http</artifactId>
3843
<version>9.4.32.v20200930</version>
3944
</dependency>
45+
<dependency>
46+
<groupId>commons-io</groupId>
47+
<artifactId>commons-io</artifactId>
48+
<version>2.8.0</version>
49+
</dependency>
4050

4151
<dependency>
4252
<groupId>org.xmlunit</groupId>

jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/DslTestPlan.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.apache.jmeter.control.gui.TestPlanGui;
88
import org.apache.jmeter.testelement.TestElement;
99
import org.apache.jmeter.testelement.TestPlan;
10+
import org.apache.jorphan.collections.HashTree;
1011
import us.abstracta.jmeter.javadsl.core.DslTestPlan.TestPlanChild;
1112

1213
/**
@@ -55,7 +56,32 @@ public TestPlanStats runIn(DslJmeterEngine engine)
5556
* @throws IOException when there is a problem saving to the file.
5657
*/
5758
public void saveAsJmx(String filePath) throws IOException {
58-
new EmbeddedJmeterEngine().saveToJmx(filePath, this);
59+
EmbeddedJmeterEngine.saveTestPlanToJmx(this, filePath);
60+
}
61+
62+
public static DslTestPlan fromJmx(String filePath) throws IOException {
63+
return EmbeddedJmeterEngine.loadTestPlanFromJmx(filePath);
64+
}
65+
66+
public static DslTestPlan fromTree(HashTree tree) {
67+
return new JmxTestPlan(tree);
68+
}
69+
70+
private static class JmxTestPlan extends DslTestPlan {
71+
72+
private final HashTree tree;
73+
74+
private JmxTestPlan(HashTree tree) {
75+
super(null);
76+
this.tree = tree;
77+
}
78+
79+
@Override
80+
public HashTree buildTreeUnder(HashTree parent) {
81+
parent.putAll(tree);
82+
return tree.values().iterator().next();
83+
}
84+
5985
}
6086

6187
/**

jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/EmbeddedJmeterEngine.java

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.net.URISyntaxException;
88
import java.nio.file.Files;
99
import java.nio.file.Path;
10+
import org.apache.commons.io.FileUtils;
1011
import org.apache.jmeter.engine.StandardJMeterEngine;
1112
import org.apache.jmeter.functions.EvalFunction;
1213
import org.apache.jmeter.reporters.ResultCollector;
@@ -42,28 +43,27 @@ public TestPlanStats run(DslTestPlan testPlan) throws IOException {
4243

4344
private static class JMeterEnvironment implements Closeable {
4445

45-
private final Path propsFilePath;
46+
private final File propsDir;
4647

4748
private JMeterEnvironment() throws IOException {
48-
propsFilePath = Files.createTempFile("jmeter", ".properties");
49+
propsDir = Files.createTempDirectory("jmeter-java-dsl").toFile();
4950
try {
50-
setupJMeterProperties(propsFilePath);
51+
setupJMeterProperties(propsDir);
5152
} catch (IOException | RuntimeException e) {
52-
deleteFile(propsFilePath);
53+
FileUtils.deleteDirectory(propsDir);
5354
throw e;
5455
}
5556
}
5657

57-
private void setupJMeterProperties(Path propsFilePath) throws IOException {
58-
deleteFile(propsFilePath);
59-
Files.copy(getClass().getResourceAsStream("/saveservice.properties"), propsFilePath);
60-
JMeterUtils.loadJMeterProperties(propsFilePath.toString());
58+
private void setupJMeterProperties(File propsDir) throws IOException {
59+
File propsFile = new File(propsDir, "jmeter.properties");
60+
propsFile.createNewFile();
61+
JMeterUtils.loadJMeterProperties(propsFile.toString());
6162
JMeterUtils.setProperty("search_paths", getFunctionsJarPath());
62-
JMeterUtils.setProperty("saveservice_properties", propsFilePath.toString());
63-
}
64-
65-
private void deleteFile(Path propsFilePath) {
66-
propsFilePath.toFile().delete();
63+
JMeterUtils.setProperty("saveservice_properties",
64+
installPropertiesFile(propsDir, "saveservice.properties"));
65+
JMeterUtils.setProperty("upgrade_properties",
66+
installPropertiesFile(propsDir, "upgrade.properties"));
6767
}
6868

6969
private String getFunctionsJarPath() {
@@ -75,9 +75,16 @@ private String getFunctionsJarPath() {
7575
}
7676
}
7777

78+
private String installPropertiesFile(File propsDir, String propsFileName)
79+
throws IOException {
80+
Path saveServicePropsPath = propsDir.toPath().resolve(propsFileName);
81+
Files.copy(getClass().getResourceAsStream("/bin/" + propsFileName), saveServicePropsPath);
82+
return saveServicePropsPath.toString();
83+
}
84+
7885
@Override
79-
public void close() {
80-
deleteFile(propsFilePath);
86+
public void close() throws IOException {
87+
FileUtils.deleteDirectory(propsDir);
8188
}
8289

8390
}
@@ -106,7 +113,8 @@ private void addTestSummariserToTree(HashTree tree) {
106113
tree.add(new ResultCollector(new Summariser()));
107114
}
108115

109-
public void saveToJmx(String filePath, DslTestPlan dslTestPlan) throws IOException {
116+
public static void saveTestPlanToJmx(DslTestPlan dslTestPlan, String filePath)
117+
throws IOException {
110118
try (JMeterEnvironment env = new JMeterEnvironment();
111119
FileOutputStream output = new FileOutputStream(filePath)) {
112120
HashTree tree = new ListedHashTree();
@@ -115,4 +123,11 @@ public void saveToJmx(String filePath, DslTestPlan dslTestPlan) throws IOExcepti
115123
}
116124
}
117125

126+
public static DslTestPlan loadTestPlanFromJmx(String filePath) throws IOException {
127+
try (JMeterEnvironment env = new JMeterEnvironment()) {
128+
HashTree tree = SaveService.loadTree(new File(filePath));
129+
return DslTestPlan.fromTree(tree);
130+
}
131+
}
132+
118133
}

0 commit comments

Comments
 (0)