39

I want to use the API provided by Apache JMeter to create and run test scripts from a Java program. I have understood the basics of ThreadGroup and Samplers. I can create those in my Java class by using the JMeter API.

ThreadGroup threadGroup = new ThreadGroup();
    LoopController lc = new LoopController();
    lc.setLoops(5);
    lc.setContinueForever(true);
    threadGroup.setSamplerController(lc);
    threadGroup.setNumThreads(5);
    threadGroup.setRampUp(1);

HTTPSampler sampler = new HTTPSampler();
    sampler.setDomain("localhost");
    sampler.setPort(8080);
    sampler.setPath("/jpetstore/shop/viewCategory.shtml");
    sampler.setMethod("GET");

    Arguments arg = new Arguments();
    arg.addArgument("categoryId", "FISH");

    sampler.setArguments(arg);

However, I am not getting any idea on how to create a test script combining the thread group and sampler and then execute it from the same program. Any ideas?

Alim Ul Gias
  • 6,351
  • 2
  • 28
  • 39

5 Answers5

59

If I understand correctly, you want to run an entire test plan programmatically from within a Java program. Personally, I find it easier to create a test plan .JMX file and run it in JMeter non-GUI mode :)

Here is a simple Java example based on the controller and sampler used in the original question.

import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.threads.SetupThreadGroup;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;

public class JMeterTestFromCode {

    public static void main(String[] args){
        // Engine
        StandardJMeterEngine jm = new StandardJMeterEngine();
        // jmeter.properties
        JMeterUtils.loadJMeterProperties("c:/tmp/jmeter.properties");

        HashTree hashTree = new HashTree();     

        // HTTP Sampler
        HTTPSampler httpSampler = new HTTPSampler();
        httpSampler.setDomain("www.google.com");
        httpSampler.setPort(80);
        httpSampler.setPath("/");
        httpSampler.setMethod("GET");

        // Loop Controller
        TestElement loopCtrl = new LoopController();
        ((LoopController)loopCtrl).setLoops(1);
        ((LoopController)loopCtrl).addTestElement(httpSampler);
        ((LoopController)loopCtrl).setFirst(true);

        // Thread Group
        SetupThreadGroup threadGroup = new SetupThreadGroup();
        threadGroup.setNumThreads(1);
        threadGroup.setRampUp(1);
        threadGroup.setSamplerController((LoopController)loopCtrl);

        // Test plan
        TestPlan testPlan = new TestPlan("MY TEST PLAN");

        hashTree.add("testPlan", testPlan);
        hashTree.add("loopCtrl", loopCtrl);
        hashTree.add("threadGroup", threadGroup);
        hashTree.add("httpSampler", httpSampler);       

        jm.configure(hashTree);

        jm.run();
    }
}

Dependencies

These are the bare mininum JARs required based on JMeter 2.9 and the HTTPSampler used. Other samplers will most likely have different library JAR dependencies.

  • ApacheJMeter_core.jar
  • jorphan.jar
  • avalon-framework-4.1.4.jar
  • ApacheJMeter_http.jar
  • commons-logging-1.1.1.jar
  • logkit-2.0.jar
  • oro-2.0.8.jar
  • commons-io-2.2.jar
  • commons-lang3-3.1.jar

Note

  • I also hardwired the path to jmeter.properties in c:\tmp on Windows after first copying it from the JMeter installation /bin directory.
  • I wasn't sure how to set a forward proxy for the HTTPSampler.
T.P.
  • 2,975
  • 1
  • 21
  • 20
  • Thanks for the answer. This is exactly what I was looking for. Just one more thing. I think adding the LoopController to the ThreadGroup is enough. Adding it separately to the HashTree is not necessary. – Alim Ul Gias Oct 08 '13 at 20:19
  • Good point on the additions to the HashTree. I was playing around with the JMeter API a little while back but I'm a bit rusty on the details of data structure passed into the JMeter Engine configure method :) – T.P. Oct 09 '13 at 03:12
  • Hi.I am getting the following error. 2014-02-16 18:22:20.957 [jorphan.] (): strPathsOrJars[0] : null/lib/ext DEBUG 2014-02-16 18:22:20.957 [jorphan.] (): Did not find: C:/Users/mvandrangi/workspace/mani/bin DEBUG 2014-02-16 18:22:20.957 [jorphan.] (): Did not find: C:/Users/mvandrangi/Downloads/apache-jmeter-2.11/bin/ApacheJMeter.jar DEBUG 2014-02-16 18:22:20.957 [jorphan.] (): Did not find: C:/Users/mvandrangi/Downloads/apache-jmeter-2.11/bin/ApacheJMeter_core.jar – mani deepak Feb 17 '14 at 04:16
  • Interesting. I get the same jorphan DEBUG messages but the program is executing the test plan correctly. I believe that JOrphan is trying to locate those jar files in the JDK/jre/lib/ext directory. – T.P. Feb 17 '14 at 20:43
  • @Peperoncini will the code automatically generate jmx file or should we have to have a jmx file? – mani deepak Feb 24 '14 at 04:00
  • [jmeter.e](): About to replace in property of type:class org.apache.jmeter.testelement.property.BooleanProperty:false I am getting these errors – mani deepak Feb 25 '14 at 05:41
  • @manideepak - This particular sample does not use a .JMX file at all. The OP desired a means to create and run a JMeter test case using Java code only. Running this after first compiling it will generate a lot of logging output messages, and I would be more concerned about the actual ERROR messages as opposed to the DEBUG and INFO messages. Are you able to run the above Java code in your environment? – T.P. Feb 25 '14 at 21:58
  • @Peperoncini We are getting no error messages but [jorphan.] ():searchPathsOrJars : [null/lib/ext] and also StrPathOfJars: null/lib/ext although we have set the path of JMETER_HOME – mani deepak Feb 26 '14 at 06:15
  • I am not positive that the JMETER_HOME environment variable will be sufficient to find all of the required JAR files, since it generally points to the top directory of the JMeter installation. Have you tried setting CLASSPATH to include all of the JARs listed under "Dependencies" in this post? When I created this code snippet, I had the Java source and all of the above mentioned JAR files in the same working directory (copied from the JMeter installation directory). – T.P. Feb 26 '14 at 23:53
  • [Here is the list of files. r.cmd and c.cmd are shown in next comment. FYI: I'm running on Windows XP using JDK 6] -- ApacheJMeter_core.jar ApacheJMeter_http.jar avalon-framework-4.1.4.jar c.cmd commons-io-2.2.jar commons-lang3-3.1.jar commons-logging-1.1.1.jar JMeterTestFromCode.class JMeterTestFromCode.java jorphan.jar logkit-2.0.jar oro-2.0.8.jar r.cmd – T.P. Feb 26 '14 at 23:54
  • [Contents of the compilation batch script "c.cmd"] -- javac -cp .;ApacheJmeter_core.jar;commons-io-2.2.jar;commons-lang3-3.1.jar;jorphan.jar;commons-logging-1.1.1.jar;logkit-2.0.jar;oro-2.0.8.jar;avalon-framework-4.1.4.jar;ApacheJmeter_http.jar JMeterTestFromCode.java – T.P. Feb 26 '14 at 23:55
  • [Contents of the execution batch script "r.cmd"] -- java -cp .;ApacheJmeter_core.jar;commons-io-2.2.jar;commons-lang3-3.1.jar;jorphan.jar;commons-logging-1.1.1.jar;logkit-2.0.jar;oro-2.0.8.jar;avalon-framework-4.1.4.jar;ApacheJmeter_http.jar JMeterTestFromCode – T.P. Feb 26 '14 at 23:57
  • @Peperoncini Yes. I included all my jar files but we are getting the above messages. I tried in eclipse.I set the path in jmeter.properties file also. Had u tried in an IDE or from scratch?Can u share ur project with me? – mani deepak Feb 27 '14 at 04:58
  • @manideepak I was not using an IDE. Everything was compiled and run from the DOS Prompt on Windows XP using JDK 6. Everything I used is here in this post and comments. Hope that helps. – T.P. Feb 27 '14 at 06:39
  • @Peperoncini I tried all the jars mentioned previously and still got the debub messages as "Wont replace 3"(setNumThreads) in jm.run() – mani deepak Feb 27 '14 at 06:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/48546/discussion-between-peperoncini-and-mani-deepak) – T.P. Feb 27 '14 at 07:54
  • @JTP, thanx a lot for the code, but how can i run this if i want to run Distributed Testing? – Vishal Puliani Jan 05 '17 at 06:41
  • 1
    @VishalPuliani I'm not sure if it is even possible or practical to use the standalone Java program for distributed testing. – T.P. Jan 05 '17 at 16:35
  • 1
    I copy-pasted this example to my maven project, in which I have dependencies for ApacheJMeter_core and ApacheJMeter_http. What happens to me is that the sampler is never called. I investigated why and it seems that the hash tree being cloned into the JMeterThread is cloned absent the LoopController. So at this part: `Sampler e = this.threadGroupLoopController.next(); while(true) { while(this.running && e != null) {` it does not execute the loop and the sampler is not called. What gives? – Nom1fan Feb 15 '17 at 08:23
  • How to specify log file? I ran without any exception but also no results/log. – Lei Yang Aug 02 '19 at 06:53
14
package jMeter;

import java.io.File;
import java.io.FileOutputStream;

import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.control.gui.LoopControlPanel;
import org.apache.jmeter.control.gui.TestPlanGui;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.reporters.Summariser;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.threads.gui.ThreadGroupGui;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;

public class JMeterFromScratch {

        public static void main(String[] argv) throws Exception {

            String jmeterHome1 = "/home/ksahu/apache-jmeter-2.13";
            File jmeterHome=new File(jmeterHome1);
//          JMeterUtils.setJMeterHome(jmeterHome);
            String slash = System.getProperty("file.separator");

            if (jmeterHome.exists()) {
                File jmeterProperties = new File(jmeterHome.getPath() + slash + "bin" + slash + "jmeter.properties");
                if (jmeterProperties.exists()) {
                    //JMeter Engine
                    StandardJMeterEngine jmeter = new StandardJMeterEngine();

                    //JMeter initialization (properties, log levels, locale, etc)
                    JMeterUtils.setJMeterHome(jmeterHome.getPath());
                    JMeterUtils.loadJMeterProperties(jmeterProperties.getPath());
                    JMeterUtils.initLogging();// you can comment this line out to see extra log messages of i.e. DEBUG level
                    JMeterUtils.initLocale();

                    // JMeter Test Plan, basically JOrphan HashTree
                    HashTree testPlanTree = new HashTree();

                    // First HTTP Sampler - open example.com
                    HTTPSamplerProxy examplecomSampler = new HTTPSamplerProxy();
                    examplecomSampler.setDomain("www.google.com");
                    examplecomSampler.setPort(80);
                    examplecomSampler.setPath("/");
                    examplecomSampler.setMethod("GET");
                    examplecomSampler.setName("Open example.com");
                    examplecomSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
                    examplecomSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());


                    // Second HTTP Sampler - open blazemeter.com
                    HTTPSamplerProxy blazemetercomSampler = new HTTPSamplerProxy();
                    blazemetercomSampler.setDomain("www.tripodtech.net");
                    blazemetercomSampler.setPort(80);
                    blazemetercomSampler.setPath("/");
                    blazemetercomSampler.setMethod("GET");
                    blazemetercomSampler.setName("Open blazemeter.com");
                    blazemetercomSampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
                    blazemetercomSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName());


                    // Loop Controller
                    LoopController loopController = new LoopController();
                    loopController.setLoops(1);
                    loopController.setFirst(true);
                    loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName());
                    loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName());
                    loopController.initialize();

                    // Thread Group
                    ThreadGroup threadGroup = new ThreadGroup();
                    threadGroup.setName("Example Thread Group");
                    threadGroup.setNumThreads(1);
                    threadGroup.setRampUp(1);
                    threadGroup.setSamplerController(loopController);
                    threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName());
                    threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName());

                    // Test Plan
                    TestPlan testPlan = new TestPlan("Create JMeter Script From Java Code");
                    testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName());
                    testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName());
                    testPlan.setUserDefinedVariables((Arguments) new ArgumentsPanel().createTestElement());

                    // Construct Test Plan from previously initialized elements
                    testPlanTree.add(testPlan);
                    HashTree threadGroupHashTree = testPlanTree.add(testPlan, threadGroup);
                    threadGroupHashTree.add(blazemetercomSampler);
                    threadGroupHashTree.add(examplecomSampler);

                    // save generated test plan to JMeter's .jmx file format
                    SaveService.saveTree(testPlanTree, new FileOutputStream(jmeterHome + slash + "example.jmx"));

                    //add Summarizer output to get test progress in stdout like:
                    // summary =      2 in   1.3s =    1.5/s Avg:   631 Min:   290 Max:   973 Err:     0 (0.00%)
                    Summariser summer = null;
                    String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary");
                    if (summariserName.length() > 0) {
                        summer = new Summariser(summariserName);
                    }


                    // Store execution results into a .jtl file
                    String logFile = jmeterHome + slash + "example.jtl";
                    ResultCollector logger = new ResultCollector(summer);
                    logger.setFilename(logFile);
                    testPlanTree.add(testPlanTree.getArray()[0], logger);

                    // Run Test Plan
                    jmeter.configure(testPlanTree);
                    jmeter.run();

                    System.out.println("Test completed. See " + jmeterHome + slash + "example.jtl file for results");
                    System.out.println("JMeter .jmx script is available at " + jmeterHome + slash + "example.jmx");
                    System.exit(0);

                }
            }

            System.err.println("jmeter.home property is not set or pointing to incorrect location");
            System.exit(1);


        }
    }
7

As of August 2020, you can try using this library :

Using Maven, add to pom.xml:

<dependency>
   <groupId>us.abstracta.jmeter</groupId>
   <projectId>jmeter-java-dsl</projectId>
   <version>0.1</version>
 </dependency>

Sample code:

 import static org.assertj.core.api.Assertions.assertThat;
 import static us.abstracta.jmeter.javadsl.JmeterDsl.*;

 import java.time.Duration;
 import org.eclipse.jetty.http.MimeTypes.Type;
 import org.junit.jupiter.api.Test;
 import us.abstracta.jmeter.javadsl.TestPlanStats;

 public class PerformanceTest {

   @Test
   public void testPerformance() throws IOException {
     TestPlanStats stats = testPlan(
        threadGroup(2, 10,
        httpSampler("http://my.service")
           .post("{\"name\": \"test\"}", Type.APPLICATION_JSON)
     ),
      //this is just to log details of each request stats
     jtlWriter("test.jtl")
     ).run();
              assertThat(stats.overall().elapsedTimePercentile99()).isLessThan(Duration.ofSeconds(5));
  }

 }
UBIK LOAD PACK
  • 33,980
  • 5
  • 71
  • 116
5

I created a simple prove of concept project utilising JMeter Java Api with Maven dependences: https://github.com/piotrbo/jmeterpoc

You can either generate JMeter project jmx file and execute it from command line or execute it directly from java code.

It was tricky a bit as jmx file requires existence of 'guiclass' attribute for every TestElement. To execute jmx, it is enough to add guiclass (even with incorrect value). To open in JMeter GUI it requires to put correct value for each guiclass.

Much more annoying problem are condition based flow Controllers. JMeter API does not gives you much more then GUI. You still need to pass a condition e.g. in IfController as regular String. An the string should contain javascript. So you have Java based project with javascript with e.g. syntax error an you will not know it until you execute your performance test :-(

Probably a better alternative to stay with code and support of IDE instead JMeter GUI is to learn Scala a bit and use http://gatling.io/

Piotr Boho
  • 2,650
  • 2
  • 13
  • 20
1

Running in Non GUI mode is much more faster. Have made one project which is using Jmeter in backend mode and then parsing the XML file to display test results. Have a look at this repo- https://github.com/rohitjaryal/RESTApiAutomation.git

rohit.jaryal
  • 320
  • 1
  • 8