Search code examples
groovygradlespockgebsaucelabs

Quit driver after each Geb Spock test


I am running Geb/Spock tests in Sauce Connect, and I would prefer to have unique instances of the RemoteWebDriver per test. This way, the Sauce reports would be divided by test, which makes it easy to diagnose failures. I'm not concerned (right now) about the additional performance overhead, because as it stands running all our Geb tests via one RemoteWebDriver instance is not helpful at all - it takes a very long time to coordinate the results with the Sauce screenshots/screencasts, and when timeouts occur (which is a high possibility in a long running job over Sauce Connect) there is usually some test failure spillover.

I tried this in a class that extends GebReportingSpec:

def cleanup() {
    if (System.getProperty('geb.env')?.contains('sauce')) {
        setSauceJobStatus()
        driver.quit()
    }
}

And of course, I create a new RemoteWebDriver in the setup() method.

With this approach I get a unique Sauce Connect session per test, and the results are all beautifully organized in Sauce. HOWEVER, all the tests fail due to:

"org.openqa.selenium.remote.SessionNotFoundException: Session ID is null. Using WebDriver after calling quit()?"

It turns out that the cleanup() method in GebReportingSpec calls out to this method:

void report(String label = "") {
    browser.report(ReporterSupport.toTestReportLabel(_gebReportingSpecTestCounter, _gebReportingPerTestCounter++, _gebReportingSpecTestName.methodName, label))
}

Which throws this stack trace:

at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:125)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:572)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:622)
at org.openqa.selenium.remote.RemoteWebDriver.getPageSource(RemoteWebDriver.java:459)
at geb.report.PageSourceReporter.getPageSource(PageSourceReporter.groovy:42)
at geb.report.PageSourceReporter.writePageSource(PageSourceReporter.groovy:38)
at geb.report.PageSourceReporter.writeReport(PageSourceReporter.groovy:29)
at geb.report.CompositeReporter.writeReport(CompositeReporter.groovy:31)
at geb.Browser.report(Browser.groovy:788)
at geb.spock.GebReportingSpec.report(GebReportingSpec.groovy:44)
at geb.spock.GebReportingSpec.cleanup(GebReportingSpec.groovy:39)

It's assuming that the WebDriver instance is still around when the GebReportingSpec cleanup() method is called, so that the reporting information can be prepared.

So, my approach is obviously not the "Geb way".... I'm wondering if anyone can clue me in on how to properly create a unique driver per Spock test?


Solution

  • Unfortunately you've hit a limitation of GebReportingSpec implementation and the fixed order of execution of Spock's setup and cleanup methods in inheritance hierarchy. What you should do is to quit your browser in a method that overrides GebSpec.resetBrowser() instead of cleanup():

    void resetBrowser() {
        def driver = browser.driver
        super.resetBrowser()
        if (System.getProperty('geb.env')?.contains('sauce')) {
            driver.quit()
        }
    }
    

    Getting a local reference to the driver and then calling super method is important because calling the super method will clear browser reference which means that you won't be able to get hold of the driver after that.

    Also, you should not create a new RemoteWebDriver in setup() but you should disable driver caching which means that a new driver will be created per driver request (a driver is requested per browser creation and a new browser is created per each test) instead of the cached one being reused.