abstracta / jmeter-java-dsl

Simple JMeter performance tests API
https://abstracta.github.io/jmeter-java-dsl/
Apache License 2.0
477 stars 59 forks source link

If controller stops further execution when groovy accessed variable is used in condition check #232

Closed goran237 closed 1 year ago

goran237 commented 1 year ago

When using if controller with "${__groovy(vars['XYZ_ID'] == null)}" condition check, no further statements are being executed:

When lambda expression is applied instead s->s.vars.get("XYZ_ID")==null execution runs as expected (for both true and false conditions).

rabelenda commented 1 year ago

Hello, thank you for reaching out.

Can you share the full test plan?

I tried with this test plan:

testPlan(
        threadGroup(1,1,
            vars()
                .set("XYZ_ID", "1"),
            ifController("${__groovy(vars['XYZ_ID'] == null)}",
                httpSampler("in", "http://localhost")),
            httpSampler("out", "http://localhost")
        ),
        resultsTreeVisualizer()
    ).run();

And works as expected:

goran237 commented 1 year ago

Sure, here's the DslSimpleController that contains problematic if controller (working correctly only with Java lambda):

private DslSimpleController addVehicle(String baseUrl) {
        return simpleController("Add vehicle",
                ifController(
                        //s->s.vars.get("VEHICLE_ID")==null, <--- WORKING SOLUTION
                        "${__groovy(vars['VEHICLE_ID'] == null)}",   <--- NOT WORKING SOLUTION 
                        httpSampler("Add vehicle", baseUrl + "/api/customer-accounts/${CUSTOMER_ACCOUNT_ID}/vehicles")
                                .method(HTTPConstants.POST)
                                .contentType(ContentType.APPLICATION_JSON)
                                .body("{\n" +
                                        "\t\"brand_and_model\": \"Jeep Wrangler JKU\", \n" +
                                        "\t\"color\": \"black\", \n" +
                                        "\t\"license_plate_number\": \"IS12SNW\"\n" +
                                        "}")
                                .children(jsonExtractor("VEHICLE_ID", "data.vehicles[0].id"))));
    }
rabelenda commented 1 year ago

Thank you.

Can you share more about your test plan? For instance the part that sets VEHICLE_ID, and you also mention that samplers outside the ifcontrolller are not executed either. Maybe if you share the code where this addVehicle method is being used it could clarify your scenario.

goran237 commented 1 year ago

Sure, here the method:private DslSimpleController login(String baseUrl, String jdbcPoolName) throws InterruptedException {

      private DslSimpleController login(String baseUrl, String jdbcPoolName) throws InterruptedException {
        return simpleController("SIGN IN",
                httpSampler("Passwordless request", baseUrl + "/api/customers/passwordless-request")
                        .method(HTTPConstants.POST)
                        .contentType(ContentType.APPLICATION_JSON)
                        .body("{\n" +
                                "  \"email\": \"${REGISTERED_EMAIL}\"\n" +
                                "}"),
                jdbcSampler("GetPasswordlessCodeByEmail", jdbcPoolName,
                        "SELECT passwordless_code FROM passwordless_requests WHERE auth_identifier=? ORDER BY updated_at DESC")
                        .param("${REGISTERED_EMAIL}", Types.VARCHAR)
                        .vars("PASSWORDLESS_CODE")
                        .timeout(Duration.ofSeconds(15)),
                httpSampler("Passwordless authorize", baseUrl + "/api/customers/passwordless-authorize")
                        .method(HTTPConstants.POST)
                        .contentType(ContentType.APPLICATION_JSON)
                        .body("{\n" +
                                "\t\"passwordless_code\":\"${PASSWORDLESS_CODE_1}\",\n" +
                                "\t\"passwordless_email\":\"${REGISTERED_EMAIL}\"\n" +
                                "}")
                        .children(constantTimer(Duration.ofSeconds(12))),
                httpSampler("Passwordless login", baseUrl + "/api/customers/login")
                        .method(HTTPConstants.POST)
                        .contentType(ContentType.APPLICATION_JSON)
                        .body("{\n" +
                                "\t\"passwordless_code\":\"${PASSWORDLESS_CODE_1}\",\n" +
                                "\t\"passwordless_email\":\"${REGISTERED_EMAIL}\"\n" +
                                "}")
                        .children(
                                jsonExtractor("CUSTOMER_ACCOUNT_ID", "data.customer_account.id"),
                                jsonExtractor("TOKEN", "data.token.value"),
                                jsr223PostProcessor(c -> System.out.println(c.prevMap()))),
                httpSampler("Fetch all vehicles", baseUrl + "/api/customer-accounts/${CUSTOMER_ACCOUNT_ID}/vehicles")
                        .method(HTTPConstants.GET)
                        .contentType(ContentType.APPLICATION_JSON)
                        .children(jsonExtractor("VEHICLE_ID", "data.vehicles[0].id"))
        );
    }

This method is called first, and right after that method add vehicle (containing problematic code) is called, as shown below:

public void testPerformance() throws IOException, InterruptedException, URISyntaxException {
        String baseUrl = "https://SOME_BASE_URL";
        TestPlanStats stats = testPlan(
                vars().set("SUBCATEGORY_NAME", _SUBCATEGORY_NAME),
                csvDataSet("src/main/resources/CustomersAccounts_187K.csv")
                        .variableNames("REGISTERED_EMAIL")
                        .ignoreFirstLine(true)
                        .delimiter(",")
                        .stopThreadOnEOF(false),
                threadGroup(1, 1,
                        _HTTP_HEADERS_GLOBAL,
                        initializeApplication(baseUrl),
                        login(baseUrl),
                        addVehicle(baseUrl),
                        initialCustomerChecks(baseUrl),
                        venueDetails(baseUrl),
                        getCategories(baseUrl),
                        getSubcategories(baseUrl)
                ),
                resultsTreeVisualizer())
                .run();
}
rabelenda commented 1 year ago

Hello, I don't know what you have in initialCustomerChecks, but I tried with the following simplified version of the flow:

package us.abstracta.jmeter.javadsl;

import static us.abstracta.jmeter.javadsl.JmeterDsl.dummySampler;
import static us.abstracta.jmeter.javadsl.JmeterDsl.ifController;
import static us.abstracta.jmeter.javadsl.JmeterDsl.jsr223Sampler;
import static us.abstracta.jmeter.javadsl.JmeterDsl.resultsTreeVisualizer;
import static us.abstracta.jmeter.javadsl.JmeterDsl.simpleController;
import static us.abstracta.jmeter.javadsl.JmeterDsl.testPlan;
import static us.abstracta.jmeter.javadsl.JmeterDsl.threadGroup;

import org.junit.jupiter.api.Test;
import us.abstracta.jmeter.javadsl.core.controllers.DslSimpleController;

public class PerformanceTest {

  private DslSimpleController login() {
    return simpleController("SIGN IN",
        dummySampler("LOGIN", "OK"),
        jsr223Sampler(s -> s.vars.put("VEHICLE_ID", "1"))
    );
  }

  private DslSimpleController addVehicle() {
    return simpleController("Add vehicle",
        ifController(
            "${__groovy(vars['VEHICLE_ID'] == null)}",
            dummySampler("INNER", "OK")
        )
    );
  }

  private DslSimpleController initialCustomerChecks() {
    return simpleController("Customer Checks",
        dummySampler("OUTER", "OK")
    );
  }

  @Test
  public void test() throws Exception {
    testPlan(
        threadGroup(1, 1,
            login(),
            addVehicle(),
            initialCustomerChecks()
        ),
        resultsTreeVisualizer()
    ).run();
  }

}

And is working fine:

Maybe you are experiencing something related to the extracted variable value?

Have you tried debugging the code? For example adding a jsr223Sampler with a groovy script logging the value of vars['VEHICLE_ID']? Maybe the value you are expecting is not the actual one returned by the extractor?

rabelenda commented 1 year ago

@goran237 any news on this?

goran237 commented 1 year ago

@rabelenda As described in the initial issue and further comments, it still doesn't work.

private DslSimpleController addVehicle(String baseUrl) {
        return simpleController("Add vehicle",
                ifController(
                        //s->s.vars.get("VEHICLE_ID")==null, <--- WORKING SOLUTION
                        "${__groovy(vars['VEHICLE_ID'] == null)}",   <--- NOT WORKING SOLUTION 
                        httpSampler("Add vehicle", baseUrl + "/api/customer-accounts/${CUSTOMER_ACCOUNT_ID}/vehicles")
                                .method(HTTPConstants.POST)
                                .contentType(ContentType.APPLICATION_JSON)
                                .body("{\n" +
                                        "\t\"brand_and_model\": \"Jeep Wrangler JKU\", \n" +
                                        "\t\"color\": \"black\", \n" +
                                        "\t\"license_plate_number\": \"IS12SNW\"\n" +
                                        "}")
                                .children(jsonExtractor("VEHICLE_ID", "data.vehicles[0].id"))));
    }

Yet the workaround with Java lambda expression solves the problem. Since you cannot replicate this problem, and there's a workaround - I'll close this issue.