Integration tests, part 2 (notes on Cucumber and Selenium)

(This is part 2, some learnings a few weeks into the project described here: Integration tests = secucbootCucumber + Selenium + Spring Boot)

Selenium tricks:

Use the fluent wait instead of anything else, like here waiting for the expected messages to appear.

final WebElement element = (new FluentWait<>(DriverFactory.getInstance().getDriver()))
    .withTimeout(10, TimeUnit.SECONDS).pollingEvery(10, TimeUnit.MILLISECONDS)
    .ignoring(NoSuchElementException.class)
    .until(ExpectedConditions.visibilityOfElementLocated(By.className("Message")));

Waiting is fine but… try to avoid waiting for alerts! Waiting for them will severely impact the test performance – it takes at least a half a second until a NoAlertPresentException is thrown. So only do

    final Alert alt = DriverFactory.getInstance().getDriver().switchTo().alert();
    alt.accept();

in places you really really know there’s an alert coming and you must click on it. I know the documentation says that if there’s gonna be an alert(), WebDriver will accept it by default… but I found this very unreliable, some alerts were handled some not. Thus accepting them by hand can still be very useful.

Many times starting to edit a text field or opening a combo triggered a refresh of the page, apparently that’s how this particular application (Struts 1.x) always worked. As a normal user I didn’t notice it but it totally confused WebDriver. Suddenly the element I started editing was not there anymore – w00t??? The only solution I could come up with was: find and click the element, find it again with a (fluid) wait, and only then select/edit it.

Not having a name or id tag for many page elements was a real pita. Yeah, XPath works too, but it’s more fragile to future changes and it was a supplementary study topic for me I could have done without. So: any time you write web applications remember to DO use element IDs. Or names. Adding them afterwards also works.

I also tried to replace JUnit with TestNG, for which CucumberJVM has the same support. Why? Because it has that @AfterMethod handler, where I thought I can save screenshots with the name of the failed step. The switch was easy and it worked fine but I finally decided to go back to plain Junit… First, it turned out I can’t get detailed step information in the @AfterMethod – a Cucumber JVM problem because it won’t expose this kind of info. This might seem minor but when TestNG treats your whole Cucumber feature as one single test you can’t get around it. Also the TestNG Eclipse plugin is way less impressive – the JUnit view is much nicer, a detailed tree with each step results…

Now that I mentioned it: taking screenshots when you have an error is a very sexy feature of Selenium WebDriver. The code below saves the screenshots in a temp directory but going forward I’ll need to see how to save them in Jenkins’s current build artifact directory. They should thus get properly cleaned up with the build they belong to… Even here I faced a small annoyance – I couldn’t find in the documentation the file format of these screenshots (so what extension should I give them?). Instead of digging through code I just saved an extension-less file, opened it in Notepad++ then googling for its header showed it’s a PNG.

    final LocalDateTime now = LocalDateTime.now();
    final File scrFile = ((TakesScreenshot) DriverFactory.getInstance().getDriver())
        .getScreenshotAs(OutputType.FILE);
    try {
        final String fileName = baseLocation
          + now.format(DateTimeFormatter.ofPattern("yyyyMMdd.HHmmss.SSS.")) + name + ".png";
        FileUtils.copyFile(scrFile, new File(fileName));
    } catch (final IOException e) {
        e.printStackTrace(); // too bad I couldn't write it :(
    }

Refactoring tests

The WebDriver based code resides now only in the AbstractPage and every page object will only call these page element access primitives. It has also a technical reason: if the tested application ever changes the frontend framework (on this application a long time pending task) then the way to identify elements could change dramatically and the hope is only this class will need to be changed.

The page changing logic is now out of the pages themselves. When I know the application will stay on the same page, I return the this object (maybe the test will call it fluently). When I know the call will switch pages, I return nothing thus the transition will have to appear explicitly in the test step. I find it clearer this way – keeping apllication logic only in the steps while the page objects are only responsible for accessing the web elements.

Important bugfix

I realized I was using the wrong way the ThreadLocal driver object the whole time:

    // private final WebDriver driver; // WRONG WAY!
    // protected AbstractPage() {
    //     driver = DriverFactory.getInstance().getDriver();
    // }

    // correct way, every time you need the driver:
    DriverFactory.getInstance().getDriver().findElement(...

This only became apparent when I actually tried to reuse it – the driver was either gone or already in use somewhere else… well again I learned something.

The updated code is again in GitHub, as release 2:

https://github.com/sorin-costea/bdd/releases/tag/v2

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s