Skip to content

Commit

Permalink
Add support for loop controllers
Browse files Browse the repository at this point in the history
  • Loading branch information
rabelenda-abstracta committed Oct 26, 2021
1 parent 35fb8d0 commit 6ae9ee9
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 9 deletions.
4 changes: 4 additions & 0 deletions docs/classes.puml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ package core {

class DslWhileController extends DslController

class ForLoopController extends DslController {
int count
}

DslIfController --> PropertyScriptBuilder
DslWhileController --> PropertyScriptBuilder

Expand Down
44 changes: 42 additions & 2 deletions docs/guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -884,11 +884,13 @@ ifController(s -> s.vars.get("ACCOUNT_ID") != null,
Using java code (lambdas) will only work with embedded JMeter engine (no support for saving to JMX and running it in JMeter GUI, or running it with BlazeMeter). Use the first option to avoid such limitations.
:::

Check [DslIfController](../../jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/DslIfController.java) for more details.
Check [DslIfController](../../jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/DslIfController.java) and [JMeter Component documentation](https://jmeter.apache.org/usermanual/component_reference.html#If_Controller) for more details.

### Loops

If at any time you want to execute a given part of a test plan, inside a thread iteration, while a condition is met, then you can use `whileController` like in following example:
#### While Controller

If at any time you want to execute a given part of a test plan, inside a thread iteration, while a condition is met, then you can use `whileController` (internally using [JMeter While Controller](https://jmeter.apache.org/usermanual/component_reference.html#While_Controller)) like in following example:

```java
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -956,6 +958,44 @@ Default name for while controller, when not specified, is `while`.

Check [DslWhileController](../../jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/DslWhileController.java) for more details.

#### For Loop Controller

In simple scenarios where you just want to execute a fixed number of times, within a thread group iteration, a given part of the test plan, you can just use `forLoopController` (which uses [JMeter Loop Controller component](https://jmeter.apache.org/usermanual/component_reference.html#Loop_Controller)) as in following example:

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

import java.io.IOException;
import java.time.Duration;
import org.junit.jupiter.api.Test;
import us.abstracta.jmeter.javadsl.core.TestPlanStats;

public class PerformanceTest {

@Test
public void testPerformance() throws IOException {
TestPlanStats stats = testPlan(
threadGroup(2, 10,
forLoopController(5,
httpSampler("http://my.service/accounts")
)
)
).run();
assertThat(stats.overall().sampleTimePercentile99()).isLessThan(Duration.ofSeconds(5));
}

}
```

This will result in 10 * 5 = 50 requests to the given URL for each thread in the thread group.

::: tip
JMeter automatically generates a variable `__jm__<loopName>__idx` with the current index of while iteration (starting with 0) which you can use in children elements.
:::

Check [ForLoopController](../../jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/core/controllers/ForLoopController.java) for more details.

### Provide Request Parameters Programmatically per Request

With the standard DSL you can provide static values to request parameters, such as a body. However, you may also want to be able to modify your requests for each call. This is common in cases where your request creates something that must have unique values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import us.abstracta.jmeter.javadsl.core.DslTestPlan.TestPlanChild;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder.PropertyScript;
import us.abstracta.jmeter.javadsl.core.assertions.DslResponseAssertion;
import us.abstracta.jmeter.javadsl.core.configs.DslCsvDataSet;
import us.abstracta.jmeter.javadsl.core.controllers.DslIfController;
import us.abstracta.jmeter.javadsl.core.controllers.DslTransactionController;
import us.abstracta.jmeter.javadsl.core.controllers.DslWhileController;
import us.abstracta.jmeter.javadsl.core.controllers.ForLoopController;
import us.abstracta.jmeter.javadsl.core.controllers.PercentController;
import us.abstracta.jmeter.javadsl.core.listeners.DslViewResultsTree;
import us.abstracta.jmeter.javadsl.core.listeners.HtmlReporter;
Expand All @@ -28,7 +30,6 @@
import us.abstracta.jmeter.javadsl.core.preprocessors.DslJsr223PreProcessor.PreProcessorVars;
import us.abstracta.jmeter.javadsl.core.threadgroups.RpsThreadGroup;
import us.abstracta.jmeter.javadsl.core.timers.DslUniformRandomTimer;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder.PropertyScript;
import us.abstracta.jmeter.javadsl.http.DslCacheManager;
import us.abstracta.jmeter.javadsl.http.DslCookieManager;
import us.abstracta.jmeter.javadsl.http.DslHttpSampler;
Expand Down Expand Up @@ -326,6 +327,43 @@ public static DslWhileController whileController(String name, PropertyScript con
return new DslWhileController(name, condition, Arrays.asList(children));
}

/**
* Builds a Loop Controller that allows to run specific number of times the given children in each
* thread group iteration.
*
* Eg: if a thread group iterates 3 times and the Loop Controller is configured to 5, then the
* children elements will run {@code 3*5=15} times for each thread.
*
* @param count specifies the number of times to execute the children elements in each thread
* group iteration.
* @param children contains the test plan elements to execute the given number of times in each
* thread group iteration.
* @return the controller instance for further configuration and usage.
* @see ForLoopController
* @since 0.27
*/
public static ForLoopController forLoopController(int count, ThreadGroupChild... children) {
return new ForLoopController(null, count, Arrays.asList(children));
}

/**
* Same as {@link #forLoopController(int, ThreadGroupChild...)} but allowing to set a name which
* defines autogenerated variable created by JMeter containing iteration index.
*
* @param count specifies the number of times to execute the children elements in each thread
* group iteration.
* @param children contains the test plan elements to execute the given number of times in each
* thread group iteration.
* @return the controller instance for further configuration and usage.
* @see ForLoopController
* @see #forLoopController(int, ThreadGroupChild...)
* @since 0.27
*/
public static ForLoopController forLoopController(String name, int count,
ThreadGroupChild... children) {
return new ForLoopController(name, count, Arrays.asList(children));
}

/**
* Builds a Percent Controller to execute children only a given percent of times.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import org.apache.jmeter.control.IfController;
import org.apache.jmeter.control.gui.IfControllerPanel;
import org.apache.jmeter.testelement.TestElement;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;
import us.abstracta.jmeter.javadsl.core.DslScriptBuilder;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder.PropertyScript;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import org.apache.jmeter.control.WhileController;
import org.apache.jmeter.control.gui.WhileControllerGui;
import org.apache.jmeter.testelement.TestElement;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;
import us.abstracta.jmeter.javadsl.core.DslScriptBuilder;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder;
import us.abstracta.jmeter.javadsl.core.PropertyScriptBuilder.PropertyScript;

Expand All @@ -29,15 +29,15 @@ public DslWhileController(String name, String condition, List<ThreadGroupChild>
this.conditionBuilder = new PropertyScriptBuilder(condition);
}

private static String solveName(String name) {
return name != null ? name : "while";
}

public DslWhileController(String name, PropertyScript script, List<ThreadGroupChild> children) {
super(solveName(name), WhileControllerGui.class, children);
this.conditionBuilder = new PropertyScriptBuilder(script);
}

private static String solveName(String name) {
return name != null ? name : "while";
}

@Override
protected TestElement buildTestElement() {
WhileController ret = new WhileController();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package us.abstracta.jmeter.javadsl.core.controllers;

import java.util.List;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.control.gui.ForeachControlPanel;
import org.apache.jmeter.testelement.TestElement;
import us.abstracta.jmeter.javadsl.core.DslThreadGroup.ThreadGroupChild;

/**
* Allows running part of a test plan a given number of times inside one thread group iteration.
*
* Internally this uses JMeter Loop Controller.
*
* JMeter automatically creates a variable named {@code __jm__<controllerName>__idx} which contains
* the index of the iteration starting with zero.
*
* @since 0.27
*/
public class ForLoopController extends DslController {

private final int count;

public ForLoopController(String name, int count, List<ThreadGroupChild> children) {
super(name != null ? name : "for", ForeachControlPanel.class, children);
this.count = count;
}

@Override
protected TestElement buildTestElement() {
LoopController ret = new LoopController();
ret.setLoops(count);
return ret;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package us.abstracta.jmeter.javadsl.core.controllers;

import static org.assertj.core.api.Assertions.assertThat;
import static us.abstracta.jmeter.javadsl.JmeterDsl.forLoopController;
import static us.abstracta.jmeter.javadsl.JmeterDsl.httpSampler;
import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan;
import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup;

import com.github.tomakehurst.wiremock.client.WireMock;
import org.junit.jupiter.api.Test;
import us.abstracta.jmeter.javadsl.JmeterDslTest;
import us.abstracta.jmeter.javadsl.core.TestPlanStats;

public class ForLoopControllerTest extends JmeterDslTest {

private static final int LOOP_ITERATIONS = 3;

@Test
public void shouldExecuteExpectedNumberOfTimesWhenLoopControllerInPlan() throws Exception {
TestPlanStats stats = testPlan(
threadGroup(1, TEST_ITERATIONS,
forLoopController(LOOP_ITERATIONS,
httpSampler(wiremockUri)
)
)
).run();
assertThat(stats.overall().samplesCount()).isEqualTo(TEST_ITERATIONS * LOOP_ITERATIONS);
}

@Test
public void shouldExposeJMeterVariableWithControllerNameWhenLoopControllerInPlan()
throws Exception {
String loopName = "COUNT";
TestPlanStats stats = testPlan(
threadGroup(1, 1,
forLoopController(loopName, LOOP_ITERATIONS,
httpSampler(wiremockUri + "/${__jm__" + loopName + "__idx}")
)
)
).run();
verifyRequestMadeForPath("/0");
verifyRequestMadeForPath("/1");
verifyRequestMadeForPath("/2");
}

private void verifyRequestMadeForPath(String path) {
wiremockServer.verify(WireMock.getRequestedFor(WireMock.urlPathEqualTo(path)));
}

}

0 comments on commit 6ae9ee9

Please sign in to comment.