├── .gitignore ├── README.md ├── pom.xml └── src ├── it ├── java │ └── com │ │ └── example │ │ └── jmeter │ │ └── JmeterTestPlanIT.java ├── resources │ └── Names.csv ├── resources1.html ├── resources2.html ├── resources3.html ├── resources4.html └── resources5.html └── main ├── java └── com │ └── example │ └── jmeter │ └── JmeterTestPlan.java └── resources ├── Results.csv └── jmxFile.jmx /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | #IntelliJ files 11 | *.iml 12 | *.idea 13 | 14 | #Target 15 | target/ 16 | 17 | # Mobile Tools for Java (J2ME) 18 | .mtj.tmp/ 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.nar 24 | *.ear 25 | *.zip 26 | *.tar.gz 27 | *.rar 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | .idea 32 | my-app.iml 33 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is an example of how to create an Apache JMeter testplan in the Java SDK. Essentially creating all of the UI components necessary as well so it can be loaded into the JMeter program itself as a custom script. 2 | 3 | Although an example, this repo exists to explain how a custom testplan is made, you would likely do this in cases of automation, paramaterization, source control of JMX plans, but most noteworthy would be the creation and use of a custom sampler. i.e. : https://jmeter.apache.org/api/org/apache/jmeter/protocol/java/sampler/JavaSampler.html 4 | 5 | Instead of using the default HTTP Sampler (which does not support special or dynamic auth) you can create your own Sampler, with your own Client which then enables the use of special auth such as OAuth, AWS4, etc. using regular Java code with implementation of your choice. 6 | 7 | Again because this is your code in the language of choice you have more liberty for design and implementation choices, as well as the luxury to create unit tests for your components, thus eliminating manual testing of code components in JMeter's UI tool. 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | UTF-8 6 | UTF-8 7 | 1.8 8 | 9 | 10 | jmeter-example-project 11 | example-jmeter-with-sdk 12 | 1.0-SNAPSHOT 13 | 14 | 15 | 16 | 17 | org.apache.jmeter 18 | ApacheJMeter_core 19 | 5.1.1 20 | 21 | 22 | 23 | 24 | org.apache.jmeter 25 | ApacheJMeter_components 26 | 5.1.1 27 | 28 | 29 | 30 | 31 | org.apache.jmeter 32 | ApacheJMeter_http 33 | 5.1.1 34 | 35 | 36 | 37 | 38 | 39 | org.apache.httpcomponents 40 | httpclient 41 | 4.5.4 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | com.lazerycode.jmeter 51 | jmeter-maven-plugin 52 | 2.9.0 53 | 54 | 55 | 56 | jmeter-tests 57 | 58 | jmeter 59 | 60 | 61 | 62 | 63 | jmeter-check-results 64 | 65 | results 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/it/java/com/example/jmeter/JmeterTestPlanIT.java: -------------------------------------------------------------------------------- 1 | package com.example.jmeter; 2 | 3 | import org.apache.jorphan.collections.HashTree; 4 | import org.junit.Test; 5 | 6 | import java.io.IOException; 7 | 8 | public class JmeterTestPlanIT { 9 | String domainName = "google.com"; 10 | String path = "/search?q=" + "${" + JmeterTestPlan.QUERY_PARAMETER_VAR_NAME + "}"; 11 | String inputCSVFile = "src/it/resources/names.csv"; 12 | 13 | @Test 14 | public void sendQueriesToGoogle() throws IOException { 15 | 16 | System.out.println("hello world"); 17 | 18 | JmeterTestPlan testPlanClient = new JmeterTestPlan(); 19 | 20 | HashTree createdTree = testPlanClient.createTestPLan( 21 | inputCSVFile, 22 | domainName, 23 | path, 24 | "GET", 25 | 1); 26 | 27 | testPlanClient.engineRunner(createdTree); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/it/resources/Names.csv: -------------------------------------------------------------------------------- 1 | James 2 | Josephine 3 | Art 4 | Lenna 5 | Donette 6 | Simona 7 | Mitsue 8 | Leota 9 | Sage 10 | Kris 11 | Minna 12 | Abel 13 | Kiley 14 | Graciela 15 | Cammy 16 | Mattie 17 | Meaghan 18 | Gladys 19 | Yuki 20 | Fletche -------------------------------------------------------------------------------- /src/it/resources1.html: -------------------------------------------------------------------------------- 1 | 2 | 301 Moved 3 |

301 Moved

4 | The document has moved 5 | here. 6 | 7 | -------------------------------------------------------------------------------- /src/it/resources2.html: -------------------------------------------------------------------------------- 1 | 2 | 301 Moved 3 |

301 Moved

4 | The document has moved 5 | here. 6 | 7 | -------------------------------------------------------------------------------- /src/it/resources3.html: -------------------------------------------------------------------------------- 1 | 2 | 301 Moved 3 |

301 Moved

4 | The document has moved 5 | here. 6 | 7 | -------------------------------------------------------------------------------- /src/it/resources4.html: -------------------------------------------------------------------------------- 1 | 2 | 301 Moved 3 |

301 Moved

4 | The document has moved 5 | here. 6 | 7 | -------------------------------------------------------------------------------- /src/it/resources5.html: -------------------------------------------------------------------------------- 1 | 2 | 301 Moved 3 |

301 Moved

4 | The document has moved 5 | here. 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/example/jmeter/JmeterTestPlan.java: -------------------------------------------------------------------------------- 1 | package com.example.jmeter; 2 | 3 | import org.apache.jmeter.config.Arguments; 4 | import org.apache.jmeter.config.CSVDataSet; 5 | import org.apache.jmeter.config.gui.ArgumentsPanel; 6 | import org.apache.jmeter.control.LoopController; 7 | import org.apache.jmeter.control.gui.TestPlanGui; 8 | import org.apache.jmeter.engine.StandardJMeterEngine; 9 | import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; 10 | import org.apache.jmeter.protocol.http.sampler.HTTPSampler; 11 | import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; 12 | import org.apache.jmeter.reporters.ResultCollector; 13 | import org.apache.jmeter.reporters.Summariser; 14 | import org.apache.jmeter.save.SaveService; 15 | import org.apache.jmeter.testbeans.gui.TestBeanGUI; 16 | import org.apache.jmeter.testelement.TestElement; 17 | import org.apache.jmeter.testelement.TestPlan; 18 | import org.apache.jmeter.threads.SetupThreadGroup; 19 | import org.apache.jmeter.threads.gui.ThreadGroupGui; 20 | import org.apache.jmeter.util.JMeterUtils; 21 | import org.apache.jorphan.collections.HashTree; 22 | 23 | import java.io.File; 24 | 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | 28 | public class JmeterTestPlan { 29 | public static final String QUERY_PARAMETER_VAR_NAME = "queryParams"; 30 | 31 | /** 32 | * Used to create Jmeter test plan, also saves testplan as a .jmx file 33 | * in resource folder 34 | */ 35 | public HashTree createTestPLan(String inputCSVFile, String domainName, String path, String httpMethod, int threadCount) throws IOException { 36 | JMeterUtils.setJMeterHome("target/jmeter"); 37 | 38 | //import the jmeter properties, as is provided 39 | JMeterUtils.loadJMeterProperties("target/jmeter/bin/jmeter.properties"); 40 | //Set locale 41 | JMeterUtils.initLocale(); 42 | 43 | //Will be used to compose the testPlan, acts as container 44 | HashTree hashTree = new HashTree(); 45 | 46 | //Going to use google.com/search?q='queryParam', load in params from CSV 47 | CSVDataSet csvConfig = new CSVDataSet(); 48 | csvConfig.setProperty("filename", inputCSVFile); 49 | csvConfig.setComment("List of query params"); 50 | csvConfig.setName("MarshalQueryParams"); 51 | 52 | csvConfig.setProperty("delimiter", "\\n"); 53 | 54 | csvConfig.setProperty("variableNames", QUERY_PARAMETER_VAR_NAME); 55 | csvConfig.setProperty("recycle", "false");//Recycle input on end of file (set to false) 56 | csvConfig.setProperty("ignoreFirstLine", "false");//Ignore first line of file 57 | csvConfig.setProperty("stopThread", true);//Stops thread on EOF 58 | csvConfig.setProperty("shareMode", "shareMode.thread"); 59 | 60 | csvConfig.setProperty(TestElement.TEST_CLASS, CSVDataSet.class.getName()); 61 | csvConfig.setProperty(TestElement.GUI_CLASS, TestBeanGUI.class.getName()); 62 | 63 | //HTTPSampler acts as the container for the HTTP request to the site. 64 | HTTPSampler httpHandler = new HTTPSampler(); 65 | httpHandler.setDomain(domainName); 66 | httpHandler.setProtocol("https"); 67 | httpHandler.setPath(path); 68 | httpHandler.setMethod(httpMethod); 69 | httpHandler.setName("GoogleSearch"); 70 | 71 | //Adding pieces to enable this to be exported to a .jmx and loaded 72 | //into Jmeter 73 | httpHandler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); 74 | httpHandler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); 75 | 76 | //LoopController, handles iteration settings 77 | LoopController loopController = new LoopController(); 78 | loopController.setLoops(LoopController.INFINITE_LOOP_COUNT); 79 | loopController.setFirst(true); 80 | loopController.initialize(); 81 | 82 | //Thread groups/user count 83 | SetupThreadGroup setupThreadGroup = new SetupThreadGroup(); 84 | setupThreadGroup.setName("GoogleTG"); 85 | setupThreadGroup.setNumThreads(threadCount); 86 | setupThreadGroup.setRampUp(1); 87 | setupThreadGroup.setSamplerController(loopController); 88 | 89 | //Adding GUI pieces for Jmeter 90 | setupThreadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); 91 | setupThreadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName()); 92 | 93 | //Create the tesPlan item 94 | TestPlan testPlan = new TestPlan("GoogleQueryTestPlan"); 95 | //Adding GUI pieces for Jmeter gui 96 | testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); 97 | testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName()); 98 | testPlan.setUserDefinedVariables((Arguments) new ArgumentsPanel().createTestElement()); 99 | 100 | hashTree.add(testPlan); 101 | 102 | HashTree groupTree = hashTree.add(testPlan, setupThreadGroup); 103 | groupTree.add(httpHandler); 104 | groupTree.add(csvConfig); 105 | 106 | //Save this tes plan as a .jmx for future reference 107 | SaveService.saveTree(hashTree, new FileOutputStream("src/main/resources/jmxFile.jmx")); 108 | 109 | //Added summarizer for logging meta info 110 | Summariser summariser = new Summariser("summaryOfResults"); 111 | 112 | //Collect results 113 | 114 | ResultCollector resultCollector = new ResultCollector(summariser); 115 | 116 | resultCollector.setFilename("src/main/resources/Results.csv"); 117 | 118 | hashTree.add(hashTree.getArray()[0], resultCollector); 119 | 120 | return hashTree; 121 | } 122 | 123 | public void engineRunner(HashTree hashTree) { 124 | //Create the Jmeter engine to be used (Similar to Android's GUI engine) 125 | StandardJMeterEngine jEngine = new StandardJMeterEngine(); 126 | 127 | jEngine.configure(hashTree); 128 | 129 | jEngine.run(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/resources/Results.csv: -------------------------------------------------------------------------------- 1 | timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect 2 | 1558305694001,618,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=James,618,0,0 3 | 1558305694623,57,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,636,0,1,1,https://google.com/search?q=Josephine,57,0,0 4 | 1558305694680,51,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,624,0,1,1,https://google.com/search?q=Art,51,0,0 5 | 1558305694732,52,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=Lenna,52,0,0 6 | 1558305694785,47,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,632,0,1,1,https://google.com/search?q=Donette,47,0,0 7 | 1558305694832,52,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,630,0,1,1,https://google.com/search?q=Simona,52,0,0 8 | 1558305694885,49,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,630,0,1,1,https://google.com/search?q=Mitsue,49,0,0 9 | 1558305694935,49,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=Leota,48,0,0 10 | 1558305694984,59,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,626,0,1,1,https://google.com/search?q=Sage,59,0,0 11 | 1558305695043,51,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,626,0,1,1,https://google.com/search?q=Kris,51,0,0 12 | 1558305695095,47,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=Minna,47,0,0 13 | 1558305695142,52,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,626,0,1,1,https://google.com/search?q=Abel,52,0,0 14 | 1558305695194,43,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=Kiley,43,0,0 15 | 1558305695237,55,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,634,0,1,1,https://google.com/search?q=Graciela,55,0,0 16 | 1558305695293,50,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,628,0,1,1,https://google.com/search?q=Cammy,50,0,0 17 | 1558305695343,51,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,630,0,1,1,https://google.com/search?q=Mattie,51,0,0 18 | 1558305695395,46,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,632,0,1,1,https://google.com/search?q=Meaghan,46,0,0 19 | 1558305695441,43,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,630,0,1,1,https://google.com/search?q=Gladys,43,0,0 20 | 1558305695485,44,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,626,0,1,1,https://google.com/search?q=Yuki,44,0,0 21 | 1558305695530,49,GoogleSearch,301,Moved Permanently,GoogleTG 1-1,text,true,,632,0,1,1,https://google.com/search?q=Fletche,49,0,0 22 | -------------------------------------------------------------------------------- /src/main/resources/jmxFile.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 1 12 | 1 13 | 14 | false 15 | -1 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | google.com 24 | https 25 | /search?q=${queryParams} 26 | GET 27 | 28 | 29 | 30 | src/it/resources/names.csv 31 | List of query params 32 | \n 33 | queryParams 34 | false 35 | false 36 | true 37 | shareMode.thread 38 | 39 | 40 | 41 | 42 | 43 | 44 | --------------------------------------------------------------------------------