Search code examples
cucumbercucumber-jvm

distributing cucmber junit5 tests in docker


I have cucumber project that is running on azure build and release pipelines.

I have installed docker and docker-compose and I am able to to open containers and grid using docker-selenium through docker-compose

I would like to distribute cucumber tests , somewhat like as explained here . But in that link , it is explained using testng and I am using junit5 with cucumber . How would I achieve that , specially the below part from the link , in conjunction with what we know about cucumber parallelism in junit5?

@Parameters({"Port"})
@BeforeClass
public void initiateDriver(String Port) throws MalformedURLException {
            if(Port.equalsIgnoreCase("9001"))
    {
        driver = new RemoteWebDriver(new URL("http:localhost:4444/wd/hub"), DesiredCapabilities.chrome());
        driver.manage().window().maximize();
    }
    else if(Port.equalsIgnoreCase("9002")){
        driver = new RemoteWebDriver(new URL("http:localhost:4444/wd/hub"), DesiredCapabilities.firefox());
        driver.manage().window().maximize();
    }
}

As I understand , if I mention cucumber.execution.parallel.enabled=true , in junit-platform.properties , it will run parallely per feature file , how do I mention port to achieve below

  1. Run each feature in one container
  2. Is there a way to distribute each scenario of different feature files in separate container ?

Solution

  • I could not find any solution for parallelizing junit5 tests at scenario level so I used testNG with courgette-jvm in conjunction with cucumber-guice. It worked out of the box and run parallel test at scenario level

    Simply inlclude similar runner class in cucumber. My tests are further using RemoteWebdriver to open multiple instances on selenium grid. Make sure grid is up and running and node is registered to the grid.

    import courgette.api.CourgetteOptions;
    import courgette.api.CourgetteRunLevel;
    import courgette.api.CucumberOptions;
    import courgette.api.testng.TestNGCourgette;
    import io.cucumber.testng.AbstractTestNGCucumberTests;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    @Test
    @CourgetteOptions(
            threads = 10,
            runLevel = CourgetteRunLevel.SCENARIO,
            rerunFailedScenarios = true,
            rerunAttempts = 1,
            showTestOutput = true,
            reportTitle = "Courgette-JVM Example",
            reportTargetDir = "build",
            environmentInfo = "browser=chrome; git_branch=master",
            cucumberOptions = @CucumberOptions(
                    features = "src/test/resources/com/test/",
                    glue = "com.test.stepdefs",                   
                    publish = true,
                    plugin = {
                            "pretty",
                            "json:target/cucumber-report/cucumber.json",
                            "html:target/cucumber-report/cucumber.html"}
            ))
    class AcceptanceIT extends TestNGCourgette {
    }
    

    RemoteWebdriver config is

    protected RemoteWebDriver createDriver() throws MalformedURLException , IOException {
                       
                     Properties properties = new Properties();                
                    ClassLoader loader = Thread.currentThread().getContextClassLoader();                
                      String hubURL = "http://192.168.1.7:65299/wd/hub";
                      System.setProperty("webdriver.gecko.driver", "/Users/amit/Desktop/amit/projects/misc/geckodriver");
                     
                        FirefoxProfile profile = new FirefoxProfile();
                        DesiredCapabilities capabilities = DesiredCapabilities.firefox();
                        capabilities.setCapability(FirefoxDriver.PROFILE, profile);
                        capabilities.setPlatform(Platform.ANY);    
                        FirefoxOptions options = new FirefoxOptions();
                        options.merge(capabilities);                 
                       driver.set(new RemoteWebDriver(new URL(hubURL),options));
                       return driver.get();
            }
    

    cucumber-guice related configs

        import io.cucumber.guice.ScenarioScoped;    
        @ScenarioScoped
              public class Global {
              public RemoteWebDriver driver;
               
              public Global() throws MalformedURLException, IOException {
                    driver = new DriverFactory().getManager();
                   // getManager() should have driver from above createDriver()
    
              }
        }
    

    Inject Global class in your stepdefs like below (do necessary imports). You can inject helper classes also and guice will have it available per scenario

     @Inject
       Global global;
     
      //then you can do
       global.driver.get(site);
    

    pom for cucumber-guice

     <dependency>
          <groupId>io.cucumber</groupId>
          <artifactId>cucumber-guice</artifactId>
          <version>${cucumber.version}</version>
          <scope>test</scope>
        </dependency>
    

    Use below config , if you want to open the grid using docker-compose and point your tests to http://localhost:65299/wd/hub . Ports are according to the below file

    version: "3"
    services:
      selenium-hub:
        image: selenium/hub:3.141.59-20210607
        container_name: selenium-hub
        ports:
          - "65299:4444"
    
      chrome:
        image: selenium/node-chrome:3.141.59-20210607
        depends_on:
          - selenium-hub
        environment:
          - HUB_HOST=selenium-hub
          - HUB_PORT=4444
    
    
      firefox:
        image: selenium/node-firefox:3.141.59-20210607
        depends_on:
          - selenium-hub
        environment:
          - HUB_HOST=selenium-hub
          - HUB_PORT=4444
        deploy:
          mode: replicated
          replicas: 7