Search code examples
androidiosjunitappiumcucumber-junit

Pass Context from JUnit Runner to Cucumber Steps


I use Cucumber with a JUnit runner. I do App UI Tests with Appium, but that should not matter for this question. The relevant part is that I want to reuse features and step definitions across iOS and Android, and pass the selenium driver instance from JUnit to the steps, as I only want to start the test server once.

I have something like this:

login.feature:

Feature: Login
@android_phone @ios_phone
Scenario: Successful login
    Given ...

CucumberIOSTest.java:

@RunWith(Cucumber.class)
@CucumberOptions(
        glue = {"classpath:my.stepdefinitions"},
        tags = {"@ios_phone,@ios_tablet"}
)
public class CucumberIOSTest {

    private static WebDriver driver;

    @BeforeClass
    public static void setUp() throws Exception {
        // setup code that starts up the ios simulator via appium 
        driver = DriverFactory.createIOSDriver();
    }

    @AfterClass
    public static void tearDown() {
        // shutdown ios simulator
    }

}

And nearly the same for Android in CucumberAndroidTest.java:

@RunWith(Cucumber.class)
@CucumberOptions(
        glue = {"classpath:my.stepdefinitions"},
        tags = {"@android_phone,@android_tablet"}
)
public class CucumberAndroidTest {

    private static WebDriver driver;

    @BeforeClass
    public static void setUp() throws Exception {
        // setup code that starts up the android simulator via appium 
        driver = DriverFactory.createAndroidDriver();
    }

    @AfterClass
    public static void tearDown() {
        // shutdown android simulator
    }

}

The steps are located in GenericSteps.java (for now just one file):

public class GenericSteps {
    public GenericSteps(WebDriver driver) {
        this.driver = driver;
    }

    @Given("^...$")
    public void ...() throws Throwable {
        ...
    }
}

How can I pass the driver from CucumberIOSTest/CucumberAndroidTest to the Steps constructor?

The problem is that only the CucumberIOSTest/CucumberAndroidTest instance knows whether I want to test the iOS or Android tags. The GenericSteps cannot know this. Also I only want to start the simulator once for all tests on each platform respectively.

I tried DI, but I did not find a way to pass the WebDriver instance I created in the JUnit Runner. Any ideas?


Solution

  • I think that the simplest solution is use Singleton design pattern. DriverFactory have to always return the same instance of WebDriver. So inside your DriverFactory add 'static WebDriver instance' and getWebDriver() method.

    public final class DriverFactory {
      private static WebDriver instance = null;
    
      public static synchronized WebDriver getWebDriver() {
          if (instance == null) {throw RuntimeException("create WebDriver first")};
          return instance;
      }
      public static synchronized WebDriver createAndroidDriver() {
          if (instance == null) { 
             // setup code that starts up the android simulator via appium 
             instance  = yourWebDriver
          }
          return instance;
      }
      ...
    }    
    

    now your GenericSteps cal look like this:

    public class GenericSteps {
      public GenericSteps() {
          this.driver = DriverFactory.getWebDriver();
      }
    
      @Given("^...$")
      public void ...() throws Throwable {
          ...
      }
    }