The Arquillian Drone 2 extension for Arquillian provides a simple way to include functional tests for an application with a web-based user interface.
Arquillian Drone 2 brings the power of WebDriver into the Arquillian framework. WebDriver serves as a language to communicate with the browser, by filling the forms, navigating the pages and validating their content.
Compared with its predecessor Arquillian Drone 1, it offers new features and tools as part of the SPI as well as multiple life cycle scopes of @Drone points as part of the API.
1. Why should I use Arquillian Drone instead of plain WebDriver?
There are many reasons why you want to do that, the most important being:
-
Life cycle management of the browser
-
Interaction with deployments and containers provided by Arquillian
-
Simple usage of multiple browsers in a single test
-
Configuration kept on a single place, outside of the Java code
-
Fully compatible with the IDE
-
Integration with mobile based browsers testing (Arquillian Droidium)
-
Integration of JavaScript test suite execution (Arquillian QUnit)
-
Compatible with WebDriver (Selenium 2) and Selenium Grids
If you are still not convinced that Arquillian Drone matches your needs, you might have a look at Arquillian Graphene 2, which is a wrapper on top of WebDriver, goes one step further and provides you a convenient way how to write tests for rich AJAX UIs with an ease, injections for Page Objects and Page Fragments and much more.
2. Getting Started
2.1. See It in Action
The following example illustrates how Arquillian Drone can be used with WebDriver:
@RunWith(Arquillian.class)
public class WebDriverTest {
static final String USERNAME = "demo";
static final String PASSWORD = "demo";
@ArquillianResource
URL contextPath;
@Drone
WebDriver driver;
/**
* Creates a testing WAR of using ShrinkWrap
*
* @return WebArchive to be tested
*/
@Deployment(testable = false)
public static WebArchive createDeployment() {
return Deployments.createDeployment();
}
@Test
@InSequence(1)
public void login() {
LoginPage page = new LoginPage(driver, contextPath);
page.login(USERNAME, PASSWORD);
}
@Test
@InSequence(2)
public void logout() {
LoginPage page = new LoginPage(driver, contextPath);
page.logout();
}
}
Notice here that the Arquillian Drone test looks similar to an Arquillian
test. There is @RunWith(Arquillian.class)
runner, a @Deployment
method and a few @Test
methods. The only new elements are
@ArquillianResource
, which is here used to inject the URL
of the
deployed application and @Drone
, which injects a WebDriver
browser,
managed for you as described in Life Cycle Scopes of @Drone points.
Even when using JUnit, Arquillian allows you to force method execution order via the @InSequence annotation. Arquillian Drone is obviously compatible with TestNG as well.
The testable=false
argument for deployment forces Arquillian to run in
client mode, that is not inside of the server where the application is
deployed.
All Drone tests must run in client mode. If you need to combine tests running inside of the server as well as on the client using single
deployments, mark the deployment as testable=true and force client
execution via the @RunAsClient annotation on every client @Test
method.
|
More details are listed in Arquillian Documentation test run modes.
For the completeness of the code, here are the deployment methods as well as the LoginPage abstraction:
public class LoginPage {
private static final By LOGGED_IN = By.xpath("//li[contains(text(),'Welcome')]");
private static final By LOGGED_OUT = By.xpath("//li[contains(text(),'Goodbye')]");
private static final By USERNAME_FIELD = By.id("loginForm:username");
private static final By PASSWORD_FIELD = By.id("loginForm:password");
private static final By LOGIN_BUTTON = By.id("loginForm:login");
private static final By LOGOUT_BUTTON = By.id("loginForm:logout");
private final WebDriver driver;
private final URL contextPath;
public LoginPage(WebDriver driver, URL contextPath) {
this.driver = driver;
this.contextPath = contextPath;
}
public void login(String name, String password) {
driver.get(contextPath + "home.jsf");
driver.findElement(USERNAME_FIELD).sendKeys(USERNAME);
driver.findElement(PASSWORD_FIELD).sendKeys(PASSWORD);
driver.findElement(LOGIN_BUTTON).click();
Assert.isTrue("User is logged in.", driver.findElement(LOGGED_IN).isDisplayed());
}
public void logout() {
driver.findElement(LOGOUT_BUTTON).click();
Assert.isTrue("User is not logged in", driver.findElement(LOGGED_OUT).isDisplayed(), "User is logged out");
}
}
public class Deployments {
public static WebArchive createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive.class)
// add classes
.addClasses(Credentials.class, LoggedIn.class, Login.class, User.class, Users.class)
// add configuration
.addAsResource("META-INF/persistence.xml", "META-INF/persistence.xml")
.addAsWebInfResource(new File("src/test/webapp/WEB-INF/beans.xml"))
.addAsWebInfResource(new File("src/test/webapp/WEB-INF/faces-config.xml"))
// add pages
.addAsWebResource(new File("src/test/webapp/index.html"))
.addAsWebResource(new File("src/test/webapp/home.xhtml"))
.addAsWebResource(new File("src/test/webapp/template.xhtml"))
.addAsWebResource(new File("src/test/webapp/users.xhtml"))
.setWebXML(new File("src/test/webapp/WEB-INF/web.xml"));
return war;
}
}
You need to put following configuration to the pom.xml file of your project:
<dependencyManagement>
<dependencies>
<!-- Arquillian Core dependencies -->
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${version.org.jboss.arquillian}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Arquillian Drone dependencies and WebDriver/Selenium dependencies -->
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>
<version>${version.org.jboss.arquillian.drone}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- To use Arquillian Graphene (2) -->
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<version>${version.org.jboss.arquillian.graphene2}</version>
<type>pom</type>
<scope>test</scope>
</dependency>
</dependencies>
You can use graphene-webdriver dependency chain even if you are willing to use plain WebDriver. Graphene will just bring you more goodies but it does not force you to change a single line of your code |
Nevertheless, if you still don’t want to add the Graphene dependencies and just use the plain WebDriver, use this Drone dependency chain:
<dependencies>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-webdriver-depchain</artifactId>
<version>${version.org.jboss.arquillian.drone}</version>
<type>pom</type>
<scope>test</scope>
</dependency>
</dependencies>
3. Automatic Download
Drone provides a functionality of automatic download of some binaries such as browser webdriver and Selenium Server jar. Which binaries and which features are supported can be found in tables below.
3.1. How it works?
Let say that I want to use Chrome for my UI tests. First of all, Drone checks if the path to the webdriver binary isn’t already set. In this case it would check a system property webdriver.chrome.driver
; if this is empty then it checks arquillian.xml property: chromeDriverBinary
. In case that both properties are empty, then the automatic download comes up.
Now we have several options how to specify which binary should be downloaded and where it should be stored. For storage purposes cache directories $HOME/.arquillian/drone/subdirectory_specific_to_binary
are used so the binaries are not downloaded over and over again.
3.1.1. Version
Using the property
<property name="chromeDriverVersion">2.10</property>
you can define which version of the Chrome webdriver should be downloaded. The binary is then cached in a directory $HOME/.arquillian/drone/chrome/2.10/
.
3.1.2. URL
Using the property
<property name="chromeDriverUrl">http://url/to/chrome/webdriver </property>
you can define which url the Chrome webdriver should be downloaded from. The directory where it is stored depends if you also specify the version of this binary or not. In case you use also the before-mentioned property chromeDriverVersion
then it is cached in corresponding directory $HOME/.arquillian/drone/chrome/specified_version/
; otherwise it is downloaded into $project.directory/target/drone/downloaded/
so no cache is used.
3.1.3. Latest
In case that you don’t define any property then Drone finds the latest version of the given binary and downloads this one. The binary (in case of Chrome webdriver) is cached at $HOME/.arquillian/drone/chrome/latest_version/
.
3.1.4. Properties, directory names and support of latest versions
Explanatory notes:
System property - intended to be used as a system property to define a path to a binary
arquillian.xml property - intended to be used as a property used in arquillian.xml file to define a path to a binary
Resolution of latest version supported - if the latest version is resolved when no property is defined
Version driver property - use this arquillian.xml property to define version of the binary that should be downloaded/used
Url driver property - use this arquillian.xml property to define an URL the binary should be downloaded from
Cache subdirectory - name of the sub-directory (withing $HOME/.arquillian/drone/
) the binaries are cached in
Architecture - in arquillian.xml file, you can explicitly specify the target architecture of the binary for selected webdrivers; if not specified it’s then resolved from the host OS
Selenium Server
System property |
selenium.server.binary.path |
arquillian.xml property |
seleniumServerBinary |
Resolution of latest version supported |
yes |
Version driver property |
seleniumServerVersion |
Url driver property |
seleniumServerUrl |
Cache subdirectory |
selenium-server |
Gecko driver (Firefox)
System property |
webdriver.gecko.driver |
arquillian.xml property |
firefoxDriverBinary |
Resolution of latest version supported |
yes |
Version driver property |
firefoxDriverVersion |
Url driver property |
firefoxDriverUrl |
Cache subdirectory |
firefox |
Chrome driver
System property |
webdriver.chrome.driver |
arquillian.xml property |
chromeDriverBinary |
Resolution of latest version supported |
yes |
Version driver property |
chromeDriverVersion |
Url driver property |
chromeDriverUrl |
Cache subdirectory |
chrome |
Edge driver
System property |
webdriver.edge.driver |
arquillian.xml property |
edgeDriverBinary |
Resolution of latest version supported |
yes |
Version driver property |
edgeDriverVersion |
Url driver property |
edgeDriverUrl |
Cache subdirectory |
edge |
IE driver
System property |
webdriver.ie.driver |
arquillian.xml property |
ieDriverBinary |
Resolution of latest version supported |
yes |
Version driver property |
ieDriverVersion |
Url driver property |
ieDriverUrl |
Cache subdirectory |
internetExplorer |
Architecture |
ieDriverArch |
The architecture of the binary can be explicitly specified because the 64 bit version of the IE driver is having issues with sendKeys
method at some specific conditions.
PhantomJS
System property |
phantomjs.binary.path |
arquillian.xml property |
phantomjsBinary |
Resolution of latest version supported |
yes |
Version driver property |
phantomjsBinaryVersion |
Url driver property |
phantomjsBinaryUrl |
Cache subdirectory |
phantomjs |
Opera driver
System property |
webdriver.opera.driver |
arquillian.xml property |
operaDriverBinary |
Resolution of latest version supported |
no |
Version driver property |
operaDriverVersion (only in combination with url) |
Url driver property |
operaDriverUrl |
Cache subdirectory |
opera |
Safari driver
System property |
- |
arquillian.xml property |
- |
Resolution of latest version supported |
no |
Version driver property |
- |
Url driver property |
no |
Cache subdirectory |
- |
We are not supporting
|
3.1.5. GitHub rate limit
In case of downloading webdrivers from GitHub, you may reach rate limit for unauthenticated requests (60 per hour). To avoid this, there has been implemented GitHub release caching functionality that caches GitHub responses based on the last modification date from the returned response header.
Authenticated requests
In some cases (larger CI matrix jobs) this response caching doesn’t have to suffice. To use authenticated requests (where the rate limit is much higher) you need to specify your GitHub username and secret token in your arquillian.xml file:
<property name="githubUsername">username</property>
<property name="githubToken">secretToken</property>
3.2. Activate / Deactivate
The automatic download functionality is activated by default; if you want to set it off use the property:
<property name="downloadBinaries">false</property>
and no binary will be downloaded.
3.3. Selenium Server
Apart from the fact that the Selenium Server binary is automatically downloaded, in the case of @RemoteWebDriver@ it is also automatically started with the correct parameters (path to the webdriver that should be used).
So, if you use RemoteWebDriver
Drone checks the address specified using property remoteAddress
in arquillian.xml file (default is: http://localhost:4444/wd/hub
) and if the address is not accessible (there isn’t running anything) then it automatically starts selenium server instance on the given address.
For example I’d like to use RemoteWebDriver
with the browser Firefox and without specifying any custom remote address, then the Selenium Server would be started using command:
java -Dwebdriver.gecko.driver=target/…/geckodriver -jar target/…/selenium-server-standalone-3.8.1.jar -port 4444
In case you would like to add some additional selenium server arguments to the command, you can use parameter seleniumServerArgs
in your arquillian.xml file.
For example, if you used these properties:
<property name="seleniumServerArgs">-debug true -role node -browserTimeout 1000</property>
<property name="browser">firefox</property>
then the command used for starting Selenium Server instance would look like:
java -Dwebdriver.gecko.driver=target/…/geckodriver -jar target/…/selenium-server-standalone-3.8.1.jar -port 4444 -debug true -role node -browserTimeout 1000
4. Supported Frameworks And Their Versions
The following frameworks are supported and tested with the latest
version of Arquillian Drone. Drone type is the type you can inject via
the @Drone
annotation.
Framework Name | Drone Type | Tested Version |
---|---|---|
WebDriver |
ChromeDriver |
2.53.1 |
Arquillian Graphene |
WebDriver |
2.1.0.Final |
It is not required to use Arquillian Drone with the exact version we
certified. You can override versions via <dependencyManagement> , as
explained in the
Arquillian
FAQ.
|
If you are in doubt what to use for a newly created project, Arquillian team recommends you to start with Graphene, which is based on WebDriver, however brings you a lot of AJAX goodies.
5. Maven Setup Example
Adding an Arquillian Drone dependency can be divided into two parts:
-
Adding a Bill of Materials (BOM) into the dependency section for both Arquillian and Arquillian Drone. This step ensures that Maven will fetch the correct version of all dependencies.
-
Adding a Dependency Chain dependency. This greatly simplifies the entry point as you only need to add a single dependency. All transitive dependencies, like the version of Selenium, will be fetched for you automatically.
The order in the <dependencyManagement> section matters. The first
version defined takes precedence. By listing Arquillian BOM before
Arquillian Drone BOM, you encore Drone to use latest Arquillian Core.
|
As for the first step, this is the same for all supported Drones:
<properties>
<version.org.jboss.arquillian>1.1.15.Final</version.org.jboss.arquillian>
<version.org.jboss.arquillian.drone>2.4.5</version.org.jboss.arquillian.drone>
<version.org.jboss.arquillian.graphene>2.3.2</version.org.jboss.arquillian.graphene>
</properties>
<dependencyManagement>
<dependencies>
<!-- Arquillian Core dependencies -->
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${version.org.jboss.arquillian}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Arquillian Drone dependencies and WebDriver/Selenium dependencies -->
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>
<version>${version.org.jboss.arquillian.drone}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
If you need to use newer Selenium version than the one used by Drone,
you can specify selenium-bom
in the dependencyManagement part as
well.
If you use selenium-bom make sure that it is specified
before the arquillian-drone-bom (or also before other BOMs that manage
Selenium version) to make the change effective.Example of Selenium BOM for Selenium 3.8.1 |
<dependencyManagement>
<dependencies>
...
<!-- Selenium BOM -->
<dependency>
<groupId>org.jboss.arquillian.selenium</groupId>
<artifactId>selenium-bom</artifactId>
<version>3.8.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencies>
</dependencyManagement>
The latter step differs based on what Drone you want to use. Include one
of the following into the <dependencies>
section:
To use Arquillian Graphene 2:
<dependency>
<groupId>org.jboss.arquillian.graphene</groupId>
<artifactId>graphene-webdriver</artifactId>
<version>${version.org.jboss.arquillian.graphene}</version>
<type>pom</type>
<scope>test</scope>
</dependency>
To use WebDriver:
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-webdriver-depchain</artifactId>
<version>${version.org.jboss.arquillian.drone}</version>
<type>pom</type>
<scope>test</scope>
</dependency>
WebDriver is a subset of Graphene. You can import Graphene and not to use any of the Graphene features from the start. However, it would be super easy to add them later on. |
6. Life Cycle Scopes of @Drone points
Arquillian Drone does not allow you to control the life cycle of web testing framework objects, but it provides three different scopes which should be sufficient for most usages required by developers. These are:
6.1. 1. Class Scoped Life Cycle
For the Drone points with the class scoped life cycle, a configuration
for the instance is created before a test class is run. This
configuration is used to properly initialize an instance of the tool.
The instance is injected into the field and holds until the last test in
the test class is finished, then it is disposed. You can think of
@BeforeClass
and @AfterClass
equivalents.
This scope is default scope for the field injection points. If you
still want to declare the Drone point to be class-scoped use the
annotation @ClassLifecycle
.
6.2. 2. Method Scoped Life Cycle
For the Drone points with the method scoped life cycle, an instance is
configured and created before Arquillian enters test method and it is
destroyed after method finishes. You can think of @Before
and @After
equivalents.
This scope is a default scope for the method parameter injection points.
To declare a field injection point as a method-scoped Drone point use
the annotation @MethodLifecycle
6.3. 3. Deployment Scoped Life Cycle
For the Drone points with the deployment scoped life cycle, an instance
is configured and created after an Arquillian deployment is deployed and
it is destroyed when the deployment is about to be undeployed. You can
think of @AfterDeploy
and @BeforeUnDeploy
equivalents.
To declare any injection point as a deployment-scoped Drone point use
the annotation @OperateOnDeployment("deployment_name")
with the
specified name of the deployment the Drone point should be tied to.
@RunWith(Arquillian.class)
public class EnrichedClass
{
@Deployment(name = "cool_deployment")
public static Archive deploy() {
return ShrinkWrap.create(Archive.class);
}
@Drone
@OperateOnDeployment("cool_deployment")
WebDriver foo;
...
}
It is important to know that you can combine multiple instances in one
test and you can have them in different scopes. You can as well combine
different framework types. Following example shows class-scoped instance
foo
and method-scoped instance baz
of type WebDriver
combined with
method-scoped bar
of type FirefoxDriver
.
@RunWith(Arquillian.class)
public class EnrichedClass
{
@Drone WebDriver foo;
@Drone @MethodLifecycle WebDriver baz;
// this will always retrieve FirefoxDriver, no matter what you specify in arquillian.xml file
@Test
public void runThisTestAlwaysWithFirefoxDriver(@Drone FirefoxDriver bar) {
...
}
}
6.3.1. Keeping Multiple Drone Instances Of The Same Field Type
With Arquillian Drone, it is possible to keep more than one instance of
a web test framework tool of the same type and determine which instance
to use in a type safe way. Arquillian Drone uses the concept of a
@Qualifier
annotation which you may know from CDI. Drone defines its
own @Qualifier
meta-annotation which allows you to create your own
annotations usable to qualify any @Drone
injections. By default, if no
@Qualifier
annotation is present, Arquillian Drone implicitly uses the
@Default
qualifier. The following code defines a new qualifying
annotation named Different
.
Take care to not accidentally import the Qualifier annotation defined by
CDI (javax.inject.Qualifier
). Drone defines its own meta-annotation of
the same name.
package org.jboss.arquillian.drone.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.jboss.arquillian.drone.api.annotation.Qualifier;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Qualifier
public @interface Different {
}
Once you have defined a qualifier, you can use it in your tests, for
example in following way, having two distinct class based life cycle
instances of WebDriver
.
@RunWith(Arquillian.class)
@RunAsClient
public class EnrichedClass {
@Drone WebDriver foo;
@Drone @Different WebDriver bar;
@Test
public void testWithBothFooAndBar() {
...
}
}
7. Configuring Drone Instances
Drone instances are automatically configured from arquillian.xml descriptor file or System properties, which take precedence. You can eventually omit the configuration altogether, if you are happy with the default values. Obviously, configurations are compatible with @Qualifier annotations, so you can create a special configuration for a method based life cycle browser if you will.
Extension qualifier must match the value listed in configuration. Otherwise Drone won’t pick the configuration.
The different configuration options supported by Drone are:
7.1. Default Drone Configuration
Drone global configuration is applied for all supported frameworks at
the same time. It uses drone
extension qualifier.
<extension qualifier="drone">
<property name="instantiationTimeoutInSeconds">120</property>
</extension>
Property Name | Default Value | Description |
---|---|---|
instantiationTimeoutInSeconds |
60 |
Default timeout in seconds to get instance of a browser. Set to |
7.2. WebDriver configuration
WebDriver uses webdriver
qualifier.
<extension qualifier="webdriver">
<property name="browser">firefox</property>
</extension>
Property Name | Default Value | Description |
---|---|---|
browser |
htmlUnit |
Determines which browser instance is created for WebDriver testing. Following values are valid:
|
iePort |
- |
Default port where to connect for Internet Explorer driver |
remoteAddress |
Default address for remote driver to connect |
|
remoteReusable |
false |
The flag which indicates that remote session should be reused between subsequent executions - gives opportunity to reuse browser window for debugging and/or test execution speed-up. |
reuseCookies |
false |
If you are using remote reusable browser, you can force it to reuse cookies |
firefoxExtensions |
- |
Path or multiple paths to xpi files that will be installed into Firefox instance as extensions. Separate paths using space, use quotes in case that path contains spaces |
firefox_profile |
- |
Path to Firefox Profile to be used instead of default one delivered with FirefoxDriver |
firefoxUserPreferences |
- |
Path to Firefox user preferences. This file will be parsed and values will be applied to freshly created Firefox profile. |
htmlUnitWebClientOptions |
- |
Semicolon separated list of webClientOptions for HtmlUnitDriver. e.g. timeout=10; javaScriptEnabled=true; throwExceptionOnScriptError=false; SSLClientProtocols=protocal1, protocal2 |
dimensions |
- |
Dimensions of browser window in |
Some additional configuration parameters related to binaries can be found in Properties, directory names and support of latest versions
If you need to enable any browser capability, simply specify it as a property in extension configuration. For instance, if you are running Firefox browser and you want to change the binary location, you can do it via following code:
<extension qualifier="webdriver">
<property name="firefox_binary">/path/to/firefox</property>
</extension>
We have enabled JavaScript for htmlUnit driver by default. If you want to disable it, configure appropriate capability to false:
<property name="javascriptEnabled">false</property>
WebDriver expects a Java Object stored in Capabilities settings for some of the WebDriver capabilities. Therefore, we provide a simple mappings to text format for some properties described in table below.
Property Name | Format |
---|---|
loggingPrefs |
Comma separated list of logging levels for FirefoxDriver. Use driver=${value1},profiler=${value2} where value is one of the following: SEVERE, WARNING, INFO, CONFIG, FINE, FINER or FINEST |
7.3. Chrome options
Drone provides you a possibility to set chrome options for a chromeDriver
through your arquillian.xml
file. You can find all possible options that can be set on this web page.
The process of setting options uses a ChromeOptions class which means that the parameter names (used in arquillian.xml
) are tightly coupled with the names of the set/add methods defined in the class. Drone expects that the name of each parameter consists of:
“chrome” + (name of the set/add method of ChromeOption class without first three chars)
the whole string should be in camel case. For example, in case of an option args which is coupled with the method addArguments, the parameter should look like this:
<property name=“chromeArguments”>--first-argument --second-argument</property>
INFO
Please notice that there are two methods named addArguments
in the ChromeOptions
class, one with a parameter which is a list of strings and second one with an array of strings - Drone treats them as a one single method (for other methods it is applied analogically).
7.3.1. Value formats
-
In the cases, when the value can be an array or list of strings/files, you should specify all of them in one string separated by space (this is also applied for extensions as well as for encoded extensions).
-
It is a little bit different in the case of experimental options. These options should be provided as set of key-value pairs, so we decided to use JSON format for it (can be in multiline format) - for example:
<property name=“chromeExperimentalOption”>
{
"perfLoggingPrefs": {
"traceCategories": ",blink.console,disabled-by-default-devtools.timeline,benchmark"
},
"prefs": {
"download.default_directory": "/usr/local/path/to/download/directory"
}
}
</property>
7.3.2. Debug
If you struggle with passing required chrome options through the arquillian.xml
file, you can use a parameter chromePrintOptions
with a value true
:
<property name=“chromePrintOptions”>true</property>
This ensures that Drone prints out the whole content of ChromeOptions
in a JSON format to the standard output.
7.4. Options for other drivers
Similarly to Chrome options, you can also configure FirefoxOptions, SafariOptions and OperaOptions classes for Firefox, Safari and Opera, respectively.
For example, to set GeckoDriver logger level, use:
<property name=“firefoxLogLevel”>SEVERE</property>
Or to use Safari Technology Preview, specify:
<property name=“safariUseTechnologyPreview”>false</property>
7.5. Graphene 2 Configuration
Graphene 2 reuses configuration specified for WebDriver, using
webdriver
qualifier. You can additionally use a
Arquillian
Graphene 2 configuration to set Graphene specific configuration, such
as default UI timeouts.
7.6. Selenium Server Configuration
Selenium server is automatically started with the parameter reflecting browser that is used (path to the webdriver). For example, in case that the browser Firefox is used and with default address, then the Selenium Server instance would be started using a command:
java -Dwebdriver.gecko.driver=target/…/geckodriver -jar target/…/selenium-server-standalone-3.8.1.jar -port 4444
In case you would like to add some additional selenium server arguments to the command, you can use parameter seleniumServerArgs
in your arquillian.xml file.
For example, if you used these properties:
<property name="seleniumServerArgs">-debug true -role node -browserTimeout 1000</property>
<property name="browser">firefox</property>
then the command used for starting Selenium Server instance would look like:
java -Dwebdriver.gecko.driver=target/…/geckodriver -jar target/…/selenium-server-standalone-3.8.1.jar -port 4444 -debug true -role node -browserTimeout 1000
8. Extended Configuration: Configuring @Qualifier’d Drone Instances
If you are wondering how to define configuration for @Qualifier
@Drone
instance, it’s very easy. Only modification you have to do is
to change qualifier to include - (@Qualifier
annotation name converted
to lowercase). For instance, if you qualified Arquillian Graphene
instance with @MyExtraBrowser, its extension qualifier will become
graphene-myextrabrowser.
Arquillian Drone configures your browser using two-step process:
-
Search for the exact match of qualifier (e.g.
graphene-myextrabrowser
) in arquillian.xml, if found, step 2 is not performed. -
Search for a match of base qualifier, without type safe
@Qualifier
(e.g.graphene
) in arquillian.xml.
Then System property are applied in the same fashion.
9. Skipping creation of @Drone instances
In case you want to skip a creation/injection of @Drone
instances you can use a system property arquillian.drone.skip.creation
with a value true
. This property is checked in a @Before
phase, so you can modify the property during the test execution.
10. Arquillian Drone SPI
The big advantage of Arquillian Drone extension is its flexibility. We provide you reasonable defaults, but if they are not sufficient or if they do not fulfill your needs, you can change them. You can change the behavior of existing implementation or implement a support for your own testing framework as well.
10.1. Event Model
Drone itself is not using Arquillian Container related event, which means that it is able to work with Arquillian Standalone test runners. Arquillian Drone itself observes following events:
Arquillian Event | Drone Default Action |
---|---|
BeforeSuite |
Drone creates a registry with all Drone SPI implementation on the
classpath |
BeforeClass |
Drone creates a configuration and future instance for Drone points with class scoped life cycle |
AfterDeploy |
Drone creates a configuration and future instance for Drone points with deployment scoped life cycle |
Before |
Drone creates a configuration for instances with method scoped life
cycle |
After |
Drone destroys an instance of method scoped Drone points |
AfterClass |
Drone destroys an instance of class scoped Drone points |
BeforeUnDeploy |
Drone destroys an instance of deployment scoped Drone points |
AfterSuite |
Drone destroys Selenium Server instance |
Arquillian Drone fires following events you can observe in your extension:
Arquillian Drone fired event | When is this event fired? |
---|---|
AfterDroneExtensionConfigured |
Fired before the global configuration is prepared |
AfterDronePrepared |
Fired after Drone configuration and Drone callable instance are created and stored in the context |
AfterDroneInstantiated |
Fired after Drone instance callable is converted into real Drone instance |
AfterDroneEnhanced |
Fired after Drone instance is enhanced by an
|
AfterDroneDeenhanced |
Fired after Drone instance is deenhanced by an |
AfterDroneDestroyed |
Fired after Drone instance is destroyed |
BeforeDroneExtensionConfigured |
Fired before the global configuration is prepared |
BeforeDronePrepared |
Fired before Drone configuration and Drone callable instance are created |
BeforeDroneInstantiated |
Fired before Drone instance callable is converted into real Drone instance |
BeforeDroneEnhanced |
Fired before Drone instance is enhanced by an |
BeforeDroneDeenhanced |
Fired before Drone instance is deenhanced by an |
BeforeDroneDestroyed |
Fired before the Drone instance will be destroyed |
DroneAugmented |
Fired after WebDriver instance is augmented to support more features. |
Events provide a class hierarchy, so you can observe their super classes if you want. |
10.2. Working with Drone Instances
If you want to support another testing framework and manage it’s lifecycle, you should implement following interfaces and register them in your own Arquillian Extension.
Drone Factory SPI:
-
Configurator<T, C>
Provides a way how to configure configurations of type C for@Drone
object of type T -
Instantiator<T, C>
Provides a way how to instantiate@Drone
object of type T with configuration C -
Destructor<T>
Provides a way how to dispose@Drone
object of type T -
DroneInstanceEnhancer<T>
Provides a way how to enhanceDrone
object of type T with additional functionality. All enhancers available on class path and compatible with current Drone type are always applied.
Drone Context SPI:
-
DroneConfiguration
This is effectively a marker for configuration of type C -
DronePoint
An unique description of a Drone in a code. -
DroneRegistry
Register of available {{Configurator}}s, {{Instantiator}}s and {{Destructor}}s discovered via SPI. -
DronePointContext
A unique holder for configuration, callable instance and metadata of each Drone point. -
DroneContext
Holder for all {{DronePointContext}}s and the global configuration. -
InstanceOrCallableInstance
Holder for any object in DroneContext. It allows to hold both real instance and callable instance in union like manner. It is also used to hold Drone related configuration, which is always instantiated
Drone WebDriver SPI:
-
BrowserCapabilitiesRegistry
Container for all registered WebDriver browser capabilities -
BrowserCapabilities
Implementation of browser implementation forWebDriver
Implementations of Configurator, Instantiator and Destructor are
searched on the class path and they are sorted according to precedence
they declare. Default implementation has precedence of 0, so if your
implementation has a higher precedence and instantiates the exact type,
Arquillian Drone will use it instead of default variant. This provides
you the ultimate way how to change behavior if desired. Of course, you
can provide support for your own framework in the very same way, so in
your test you can use @Drone annotation to inject instances of
arbitrary web testing framework.
|
11. Building the project
Prerequisites:
-
JDK 8 and newer
-
Maven 3.0.3 and newer
11.1. Running test suite
To run only unit tests:
mvn clean test
To run the whole test suite including functional tests using phantomjs
browser.
mvn clean verify -Dbrowser=phantomjs
In case you want to modify the browser, you can specify -Dbrowser=firefox
for instance. This will run the test suite using firefox defined. Similarly, you can use Chrome, Safari, etc…
VNC server instance can be used to let all the browsers pop out in separate display. Just prepend both commands with DISPLAY=:${display.number} |