Arquillian, Drone, PhantomJS are awesome tools to write UI tests but by default scope of PhantomJS is linked to JUnit scope (class or test). When having a big suite it means Drone will fork a bunch of processes and loose a lot of time starting/stopping PhantomJS…for nothing.
This should be addressed with drone 2 but here a small workaround changing the life.
First define a single fork PhantomJS driver factory (ie start a process lazily then kill it with the (test) JVM:
import org.jboss.arquillian.drone.spi.Configurator; import org.jboss.arquillian.drone.spi.Destructor; import org.jboss.arquillian.drone.spi.Instantiator; import org.jboss.arquillian.drone.webdriver.configuration.WebDriverConfiguration; import org.jboss.arquillian.drone.webdriver.factory.PhantomJSDriverFactory; import org.openqa.selenium.phantomjs.PhantomJSDriver; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; public class SingleForkPhantomJSDriverFactory extends PhantomJSDriverFactory // useless but Drone gets generics brutally so we need it implements Configurator, Instantiator, Destructor { private static final Logger LOGGER = Logger.getLogger(SingleForkPhantomJSDriverFactory.class.getName()); private static final AtomicReference DRIVER = new AtomicReference(); @Override public void destroyInstance(final PhantomJSDriver instance) { // no-op: done by a shutdown hook } @Override public PhantomJSDriver createInstance(final WebDriverConfiguration configuration) { PhantomJSDriver driver = DRIVER.get(); if (driver == null) { final PhantomJSDriver instance = super.createInstance(configuration); if (!DRIVER.compareAndSet(null, instance)) { destroyInstance(instance); driver = instance; LOGGER.info("Created PhantomJSDriver"); } else { driver = DRIVER.get(); final PhantomJSDriver theDriver = driver; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { SingleForkPhantomJSDriverFactory.super.destroyInstance(theDriver); LOGGER.info("Destroyed PhantomJSDriver"); } }); } } return driver; } }
Then we need to override default PhantomJSDriverFactory (we could define another one but we really want to override it here). To do so just define an Arquillian extension:
public class OverridePhandtomJSDriverLifecycleExtension implements LoadableExtension { @Override public void register(final ExtensionBuilder builder) { // to stay compliant with default drone behavior if used builder.override(Configurator.class, PhantomJSDriverFactory.class, SingleForkPhantomJSDriverFactory.class); builder.override(Instantiator.class, PhantomJSDriverFactory.class, SingleForkPhantomJSDriverFactory.class); builder.override(Destructor.class, PhantomJSDriverFactory.class, SingleForkPhantomJSDriverFactory.class); } }
Then register it through a META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension file (just put the qualified name of OverridePhandtomJSDriverLifecycleExtension inside).
And that’s it! now you’ll get a single PhantomJS fork for all your tests.
Tested and approved with TomEE remote adapter of course :).