2

I'm looking for some advice on how to instantiate my WebDriver driver instance in my automation framework (Selenium 3.0.1 / Java 8 / TestNG).

My goal is to handle all the instantiation code in one place, in my framework code, separated from my tests themselves, and then pass that instance around the framework.

In Ruby, I would do something like this:

./framework/driver.rb

def initialize
  $driver = Selenium::WebDriver.for :firefox

  # additional driver configuration
end

Then I would include the framework classes in my base test class and call my driver from there.

./tests/utilities/base_test.rb

  def setup
    Driver.new
  end

I would then have all my tests inherit from BaseTest.

In Java, I am unfamiliar with how to achieve this with access modifiers and package structure.

So far I have something like this (taken from Selenium HQ 1):

  public class Driver {

  private static ChromeDriverService service;
  private WebDriver driver;

  {@literal @BeforeClass}
  public static void createAndStartService() {
    service = new ChromeDriverService.Builder()
        .usingDriverExecutable(new File("path/to/my/chromedriver.exe"))
        .usingAnyFreePort()
        .build();
    service.start();
  }

  {@literal @AfterClass}
  public static void createAndStopService() {
    service.stop();
  }

  {@literal @Before}
  public void createDriver() {
    driver = new RemoteWebDriver(service.getUrl(),
        DesiredCapabilities.chrome());
  }

  {@literal @After}
  public void quitDriver() {
    driver.quit();
  }

  public WebDriver getDriver() {
   return driver;
  }
}

My question is, how can I pass this driver instance around my framework (for example, I want my Page objects to be able to use it). In Ruby, I triggered the creation of a global variable from my BaseTest. In Java, I've added the getter to return the driver instance. But this is problematic when I try to introduce TestNG annotations. For example, I want to create/teardown a driver before/after every test. I can get it to work, but is this best practice? Is there a better way?

For example, instead of using TestNG annotations to instantiate my driver, would it be better to use listeners? I've also heard about Guice and dependency injection -- is this a useful design pattern?

I've seen a number of tutorials, blog posts, articles, etc., on this topic, but I can't find what I'm after. Again, my main goal here is to handle all my driver instantiation in one place in my framework in such a way that I can pass that same driver instance around. It shouldn't matter whether it's a local ChromeDriver instance or a Remote WebDriver instance running on a grid. I need it to be clean and scalable.

  • Looks good to me, it is fine to create / design a browser instance for every test. – Yu Zhang Jan 27 '17 at 1:11
2

In my framework, I have a public abstract class BaseIntegration that my test extends and a public abstract class BasePage that my page objects extends too.

BaseIntegration has a WebDriver that all tests access (but one instance for each class test) and I pass this instance for my page objects through constructor. See the code below:

UPDATED: BaseIntegration:

public abstract class BaseIntegration {

  //private WebDriver driver;
  private Driver driver = new Driver();

  protected WebDriver getDriver() {
    return driver.getDriver();
  }

  private void setDriver(WebDriver driver){
    this.driver = driver;
  }

  @BeforeClass(alwaysRun = true)
  public void baseBeforeClass() {
    //WebDriver webDriver = new FirefoxDriver();
  //webDriver.manage().timeouts().implicitlyWait(BaseIntegration.TIMEOUT, TimeUnit.SECONDS);
    //webDriver.manage().window().maximize();
    //You can put other logics here to select Chrome, IE and other properties
    //setDriver(webDriver);
    driver. createDriver();
  }

  @AfterClass(alwaysRun = true)
  public void baseAfterClass() {
    //getDriver().close();
    driver.quitDriver();
  }
}

and BasePage:

public abstract class BasePage {

  private final WebDriver driver;
  private WebDriverWait driverWait;

  public BasePage(WebDriver driver){
    this.driver = driver;

    driverWait = new WebDriverWait(driver, BaseIntegration.TIMEOUT);
  }

  protected WebDriver getDriver(){
    return this.driver;
  }

  protected WebDriverWait getDriverWait() {
    return driverWait;
  }
}

Then I have my page object, for example SignInPage

public class SignInPage extends BasePage {

    public SignInPage(WebDriver driver) {
        super(driver);

        PageFactory.initElements(driver, this);
    }

    //METHODS HERE
    //To use driver, use the method getDriver() inherited from BsePage
}

And for last, the test class SignInIt.java

public class SignInIT extends BaseIntegration {

  SignInPage signIn;

@BeforeClass
protected void beforeClass() {
    this.signIn = new SignInPage(this.getDriver(), this.getPublishServer());
  }
  public void validateSignInButtonText() {
    //TEST CODE HERE
  }
}

I removed a lot of code, but the idea of Base Integration and BasePage it to give some common methods and properties for my test classes and page objects.

Each test class will instantiate its own driver, so will be one driver for all tests for each class.

  • This makes sense. However, how can I achieve my goal to handle my driver configuration in one place in my framework. For example, in BaseIntegration above where you have "CREATE AND PASS YOUR DRIVER HERE", how can I handle all the logic I have above (creating service, creating a driver instance, stopping an instance, stopping service)? My proposed solution was to create a separate Driver class, but I can't have it extend WebDriver. So how can I call that functionality from within BaseIntegration? – RSH Jan 27 '17 at 15:34
  • I've update BaseIntegration to create a driver with some configurations. And about your logic, you can put them on BaseIntegration as I did with baseBeforeMethod. Just remember to use (alwaysRun = true) (this is for TestNG runner) – Thiago Fioravante Jan 27 '17 at 15:57
  • Just to be clear, you are suggesting that I should not keep the driver configuration code inside its own Driver class. Instead, I should put it in the BaseIntegration class (which is similar to my BaseTest class) above. This goes against my intended goal (to keep driver configuration code separate), but certainly looks doable. – RSH Jan 27 '17 at 16:46
  • Oh, I got it. Well, you can try to remove these {@literal @BeforeClass} from Driverclass. Then inside BaseIntegration (your BaseTest) you call the Driver methods in the correct methods with @tags. This way you keep the Driver class and keep the test responsibilities to BaseTest – Thiago Fioravante Jan 27 '17 at 17:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.