Sorry, Scrum, a malfunctioning factory and the ‘Definition of Done’

Sorry, Scrum, a malfunctioning factory and the ‘Definition of Done’

Sorry!

Many apologies for the delay in this post, but sometimes life just gets in the way. Last week’s post was always going to be a challenging one. With 2 Birthdays, my wife returning to work from maternity leave and my 3 year old son starting at nursery it was set up to be a busy week. Then on Monday afternoon I was offered the chance to spend the next 2 days on Geoff Watts’ Certified ScrumMaster Course in London. It was thoroughly enjoyable as well as a thorough demonstration of the application of Scrum, but it rather put a whole in what little time I would have had to prepare for this Blog post. Despite my best efforts to complete it on Sunday night before my deadline but eventually I had to admit defeat and get some sleep. As if that wasn’t enough, Virgin Media managed to leave me with no Internet connection at the start of this week either!

This time:

At the end of my last post, I was unsure as to what to tackle next. I decided to attempt to use Google Guice to implement Dependency Injection. In order to do this, I decided that my first step was in fact to create a DriverFactory that I can use to generate a WebDriver for whichever browser I want to test on. Of course I could write one from scratch, but as with everything so far I thought I would try to use as much in built functionality as possible. I came across this Blog post from Vinoth Selvaraj so I thought I would use that as a basis for my work. I was particularly interested to find out about the ChromeDriverService provided by Selenium. This is something that I have not looked into before so a real chance to learn.

The DriverFactory

At present I have

//find the chromedriver and set its location in a system property
ClassLoader classLoader = getClass().getClassLoader();
String path = classLoader.getResource("chromedriver.exe").getPath();
System.setProperty("webdriver.chrome.driver", path);

//the chromeoptions we can explore later, but this will maximise it on startup
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--start-maximized");
driver = new ChromeDriver(chromeOptions);
System.out.println("Driver Started");

In the @Before sections of both of my AbstractTest classes. Obviously this is duplication (and I may want to use it again somewhere else!) Just as importantly, it will only provide a ChromeDriver and I would like to be able to support a number of different browsers (with the ability to add even more as I find the need) so I need a mechanism to launch the WebDriver of my choosing.

So I created a new infrastructure package in my main classes and start building out for my factory. (Mostly minor refactoring of Vinoth’s work)

Firstly an Enum of desired Browsers

package tech.alexontest.poftutor.infrastructure;

public enum DriverType {
    CHROME,
    EDGE,
    FIREFOX,
    IE,
    SAFARI,
}

and then an AbstractDriverManager to build my specific managers on

package tech.alexontest.poftutor.infrastructure;

import org.openqa.selenium.WebDriver;

public abstract class AbstractDriverManager {

    protected WebDriver driver;
    protected abstract void startService();
    protected abstract void stopService();
    protected abstract void createDriver();

    public void quitDriver() {
        if (null != driver) {
            driver.quit();
            driver = null;
        }

    }

    public WebDriver getDriver() {
        if (null == driver) {
            startService();
            createDriver();
        }
        return driver;
    }
}

adding the ChromeDriverManager implementation

package tech.alexontest.poftutor.infrastructure;

        import org.openqa.selenium.chrome.ChromeDriver;
        import org.openqa.selenium.chrome.ChromeDriverService;
        import org.openqa.selenium.chrome.ChromeOptions;
        import org.openqa.selenium.remote.DesiredCapabilities;

        import java.io.File;
        import java.io.IOException;

public class ChromeDriverManager extends AbstractDriverManager {

    private ChromeDriverService chromeDriverService;
    private File chromedriverExe;

    ChromeDriverManager() {
        chromedriverExe = new File(getClass().getClassLoader().getResource("chromedriver.exe").getPath());
    }

    @Override
    public void startService() {
        if (null == chromeDriverService) {
            try {
                chromeDriverService = new ChromeDriverService.Builder()
                        .usingDriverExecutable(chromedriverExe)
                        .usingAnyFreePort()
                        .build();
                chromeDriverService.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void stopService() {
        if (null != chromeDriverService && chromeDriverService.isRunning())
            chromeDriverService.stop();
    }

    @Override
    public void createDriver() {
        DesiredCapabilities capabilities = DesiredCapabilities.chrome();
        ChromeOptions options = new ChromeOptions();
        options.addArguments("test-type", "--start-maximized");
        capabilities.setCapability(ChromeOptions.CAPABILITY, options);
        System.out.println("Driver Started");
        driver = new ChromeDriver(chromeDriverService, capabilities);
    }
}

and lastly start on the DriverFactory although so far we still have only the one implementation.

package tech.alexontest.poftutor.infrastructure;

public class DriverManagerFactory {

    public static AbstractDriverManager getManager(DriverType type) {

        AbstractDriverManager driverManager;

        switch (type) {
            default:
                driverManager = new ChromeDriverManager();
                break;
        }
        return driverManager;

    }
}

switch over to using the Driver factory:

package tech.alexontest.poftutor.infrastructure;

import org.junit.After;
import org.junit.Before;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.openqa.selenium.WebDriver;

import static tech.alexontest.poftutor.infrastructure.DriverType.CHROME;

/**
 * Abstract Test class that creates a new WebDriver for Each Test.
 * Supports JUnit 4 and JUnit 5 tests.
 */
abstract public class AbstractTest {
    private WebDriver driver;
    private AbstractDriverManager driverManager;

    @Before
    @BeforeEach
    public void setup() {
        System.out.println("Preparing AbstractDriverManager");
        driverManager = DriverManagerFactory.getManager(CHROME);
        System.out.println("Getting Driver");
        driver = driverManager.getDriver();
    }

    @After
    @AfterEach
    public void teardown() {
        System.out.println("Quiting Driver");
        driverManager.quitDriver();
    }

    protected WebDriver getDriver() {
        return driver;
    }
}

Don’t forget to remove the RC3 designation from all the Junit dependencies in the build.gradle to get the release version and I updated the ChromeDriver executable whilst I was at it.

Set it off and:

Test success
Well that works!

So now all we need is to add an alternative browser implementation of the DriverManagerFactory. Let’s try Firefox:

Not so long ago you could fire off a Firefox test with no extra driver executable and minimal fuss. Sadly those days have passed and the geckodriver is now required. So let’s try repeating my ChromeDriverManager class for Firefox.

Everything was going swimmingly until the time to create the WebDriver. Specifically this line:

 driver = new ChromeDriver(chromeDriverService, capabilities);

It seems that this, using the xxxDriverService is only available as a constructor for ChromeDriver. Fiddlesticks!

By now it was too late to be trying to fix more problems. Switching to my (Srum) Product owner hat, Did I have a Potentially Releasable Increment? Honestly, No. I was not confident that this would continue to be part of my solution, as was seriously considering ditching it as a false start. It did not meet my ‘Definition of Done’ and so a WebDriverFactory goes back on the Backlog. That’s where this post ends….

Progress made this week:

  • Built in a framework for a Webdriver Factory.
  • Implemented a ChromeDriverFactory.

Lessons learnt:

  • WebDriver implementations do not all expose the same constructors. Indeed there is considerable variation between how they are all implemented. Of course that’s kind of the point of creating a provider, so that my test code doesn’t have to worry about what kind of browser it is, it has a single call to make whatever the browser.

Will I continue this next time or bin it and start again? Stay tuned….

One thought on “Sorry, Scrum, a malfunctioning factory and the ‘Definition of Done’

Comments are closed.

Comments are closed.