4

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?

jonsie_araki
  • 221
  • 3
  • 8

2 Answers2

4

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.

erdi
  • 6,730
  • 15
  • 27
  • erdi, thank you. I had been rolling with my own solution for a few days now (simply pulling out the code in GebReportingSpec and creating my own modified "GebReporting" class), but this approach is more straightforward. – jonsie_araki Apr 01 '15 at 20:34
  • For me this solution only works if I also call `CachingDriverFactory.clearCache()` after I quit the driver, otherwise the subsequent unrolled feature method throws the same exception as above. – kriegaex Dec 22 '15 at 07:25
0

If you use browser.quit(), then you will get an exception and the test will be failed. You can try the following snippet at the very beginning of your class, it should work just fine:

def setup() {       
    browser.config.cacheDriver = false
    browser.driver = browser.config.driver
}

def cleanup() {
    browser.close()
}

Cheers!

Sharif Mamun
  • 3,240
  • 5
  • 26
  • 49
  • Thanks for the answer, though I did try this. It doesn't close the Sauce session (which can only be closed by the driver closing, according to Sauce docs), which results in Sauce reporting a timeout error after it doesn't receive a command for 90 seconds. In the end, I decided to extend GebSpec instead of GebReportingSpec and simply pull out relevant code from GebReportingSpec. – jonsie_araki Mar 30 '15 at 23:51