5

Right now we're working on adopting Cucumber to run functional tests on our Java8/Spring app. We want our step definition files to remain as DRY as possible and as such plan on using the same step definitions in different feature files. Since we are using a selenium WebDriver to drive our tests, we need to share the same driver between step definitions.

To demonstrate why having multiple drivers is an issue for us, imagine a feature file that defines two steps: one to navigate to a page, and another to assert that a line appears on that page. If both steps happen to be defined in separate files, the first step definition will use its driver to navigate to the page. By the time the second step definition runs the assertion against its driver it hasn't navigated to the page (since those actions went to the other driver) and the test fails.

We tried implementing a base class (that contains the driver) that each step definition file would extend. As it turns out Cucumber instantiates an instance of each step definition class, and therefore we end up with each step definition having different WebDriver instances.

We thought about using Spring to inject an instance of the WebDriver in each step definition file, but I believe this would cause the same problem described above.

I know that the singleton pattern can be used to achieve this, but ours seems like such a common problem and the singleton pattern feels like overkill. Is this actually the right way to approach it? Or am I missing something really obvious?

Thank you in advance for your help!

davidicus
  • 630
  • 1
  • 6
  • 16

2 Answers2

8

I reccomend you to use pico-container as a dependency injection framework to use with cucumber-jvm.

With PicoContainer, you can have a 'base' class with the instance of WebDriver, and then pass this base class automactically to any other class. Or even you could pass directly the web driver if you prefer.

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-picocontainer</artifactId>
    <version>1.2.3</version>
    <scope>test</scope>
</dependency>

Example:

Base class with the instance of WebDriver:

public class ContextSteps {

   private static boolean initialized = false;

   private WebDriver driver;

   @Before
   public void setUp() throws Exception {
      if (!initialized) {
         // initialize the driver
         driver = = new FirefoxDriver();

         initialized = true;
      }
   }

   public WebDriver getDriver() {
      return driver;
   }
}

Other class who access webDriver through pico-container DI.

public class OtherClassSteps {

   private ContextSteps contextSteps;

   // PicoContainer injects class ContextSteps
   public OtherClassSteps (ContextSteps contextSteps) {
      this.contextSteps = contextSteps;
   }


   @Given("^Foo step$")
   public void fooStep() throws Throwable {
      // Access WebDriver instance
      WebDriver driver = contextSteps.getDriver();
   }
}

Hope it helps.

troig
  • 7,072
  • 4
  • 37
  • 63
  • 1
    awesome, i'll look into this and get back to you! – davidicus Jul 23 '15 at 14:34
  • Yep! We tried it out and your suggestion was right, in the end we actually ended up just using a singleton though due to project specific restrictions but your answer was totally valid. Thanks! – davidicus Aug 06 '15 at 23:36
  • Thanks! You can also take advantage of picocontainer to share information between steps, not only the web driver. Maybe this can be useful for you in other phases of the project. – troig Aug 07 '15 at 06:38
  • Hi @davidicus , I'm having the same predicament. Mind if you also share your singleton solution as an answer? – k_rollo Aug 20 '17 at 10:43
  • Hey @silver, I just dug up our old solution and posted it as an answer. I hope it's useful for you! – davidicus Aug 23 '17 at 20:54
  • Can I use the same way inside the page Objects? My test steps implementation can be found in page objects files and in the stepdefinitons(steps) contains only the methods calls. What do you Guys think? – brobee Nov 28 '17 at 22:33
4

This question is old, and I left the project shortly after asking this question, but I went back and looked at the code we put in place (using the singleton pattern) and this is what we ended up with. I forget exactly why we couldn't use pico-container (it was possibly an organizational constraint) but if you can use extra libraries I remember that solution worked well.

I will leave that as the accepted answer but hopefully this solution is useful for those who find themselves in a similar position that I was in a few years ago.

public class TestingBase {

    private static TestingBase instance;
    private static WebDriver driver;
    private static Thread CLOSE_DRIVER = new Thread() {
        @Override
        public void run() {
            driver.close();
        }

    };

    static {
        Runtime.getRuntime().addShutdownHook(CLOSE_DRIVER);
    }

    private TestingBase() {
        DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
        desiredCapabilities.setJavascriptEnabled(true);
        desiredCapabilities.setCapability("takesScreenshot", false);
        desiredCapabilities.setCapability("handlesAlerts", true);

        desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new String[]{
                "--web-security=false",
                "--ssl-protocol=TLSv1",
                "--ignore-ssl-errors=true",
                "--webdriver-loglevel=ERROR",
                "--webdriver-logfile=/var/log/phantomjs/ghostrdriver.log"
        });
        desiredCapabilities.setCapability("elementScrollBehavior",true);
        driver = new PhantomJSDriver(desiredCapabilities);
    }

    public static TestingBase getTestingBase() {
        if (instance == null) {
            instance = new TestingBase();
        }
        return instance;

    }

    public static WebDriver getDriver() {
        return getTestingBase().driver;
    }
}
davidicus
  • 630
  • 1
  • 6
  • 16
  • 1
    Hi David, that is very nice of you, thank you. I also managed to get PicoContainer working in [this](https://stackoverflow.com/questions/45780919/shared-webdriver-becomes-null-on-second-scenario-using-picocontainer) thread. – k_rollo Aug 25 '17 at 12:01
  • Nice! Glad you got it figured out – davidicus Aug 25 '17 at 15:29