Search code examples
javaseleniumcucumberbrowser-automation

Execute selenium/cucumber test in parallel, but all tests are running on the same Chrome instance


I have set up a testing framework with maven, spring-boot, cucumber(jvm5), selenium, etc. The framework was working(in parallel) OK for a long time, but I got this wired behavior recently. When I run two tests in parallel, I can see two chrome instances were started, but only one instance open the URL correctly (the other one has data; in url field). Two tests were executed on that chrome instance at the same time. (e.g. type two different usernames in the input box at the same time.) enter image description here

Any input is welcome. Thank you.

below is my pom.xml setting for parallel:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M3</version>
    <configuration>
    <parallel>methods</parallel>
    <!--<threadCount>4</threadCount>-->
    <useUnlimitedThreads>true</useUnlimitedThreads>
    <argLine>
        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
        -Xmx1024m -XX:MaxPermSize=256m
    </argLine>
    <systemPropertyVariables>
    <allure.results.directory>${project.build.directory}/allure-results
    </allure.results.directory>
    </systemPropertyVariables>
    </configuration>
    ....
</plugin>

here is the runner class:

@RunWith(Cucumber.class)
@CucumberOptions(
        strict = true,
        features = {"src/test/resources/features"},
        plugin = {
                "pretty",
                "html:target/cucumber-html-report",
                "json:target/cucumber/cucumber.json",
                "junit:target/cucumber/cucumber.xml",
                "io.qameta.allure.cucumber5jvm.AllureCucumber5Jvm",
                "rerun:target/rerun.txt"
        },
        glue = {"com.demo.bss.steps", "com.demo.bss.hook"},
        stepNotifications = true,
        monochrome = true,
        tags = {"not @deprecated"})
public class CukeTest {

}

here are the step defs:

//Globel step state class
@Component
public class GlobalStepState {

  protected WebDriver driver;
  protected WebDriverWait wait;

  protected LoginPage loginPage;
  protected CrmMainPage crmMainPage;
  protected ProdCatalogMainPage prodCataPage;

  @Value("${spring.url.bss}")
  protected String url;

}

//Global shared stepdefs
public class GlobalStepDef implements En {

  @Autowired
  GlobalStepState state;
  @Autowired
  CukeHook cukeHook;

  public GlobalStepDef() {
    Before(
        1,
        () -> {
          state.driver = cukeHook.getWebDriver();
          state.wait = new WebDriverWait(state.driver, 30);

          state.loginPage = new LoginPage(state.driver);
          state.crmMainPage = new CrmMainPage(state.driver);
          state.prodCataPage = new ProdCatalogMainPage(state.driver);
        });

    Given("^open COMARCH BSS login page$", () -> {

      state.driver.get(state.url);

    });
  }

}

//normal stepdefs
public class LoginStepDefs implements En {

  @Autowired
  private GlobalStepState state;

  public LoginStepDefs() {

    When("^I login with a user \"([^\"]*)\" and \"([^\"]*)\"$", (String email, String password) -> {
      state.loginPage.inputEmail.sendKeys(email);
      state.loginPage.inputPassword.sendKeys(password);
      state.loginPage.btnLogin.click();
    });

    Then("^the (.*) user name show display as (.*)$", (String type, String userName) -> {
      WebElement eleUserName = type.equals("CRM") ? state.crmMainPage.strCrmUserName : state.prodCataPage.strCatalogUserName;
      state.wait.until(ExpectedConditions.visibilityOf(eleUserName));
      Assert.assertEquals(userName, eleUserName.getText());
    });
  }
}

Solution

  • Components in Spring are singletons. So while you are creating two webdrivers you are assigning these to the same field of the same object. This means only one is used.

    Presumably you've started to encounter this behavior after upgrading to v5. Since v5 Cucumber will share the spring application context between threads (like all other spring tests).

    https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md#500-rc4-2019-12-21

    To ensure that a components is not shared between scenarios you have to annotate it with @ScenarioScope.

    https://github.com/cucumber/cucumber-jvm/tree/main/spring