How to create and run Apache JMeter Test Scripts from a Java program?

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?


Solution 1:

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.

Solution 2:

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);


        }
    }

Solution 3:

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

  • https://github.com/abstracta/jmeter-java-dsl

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));
  }

 }

Solution 4:

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/

Solution 5:

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