JBoss Community Archive (Read Only)

Arquillian Old

Drone

The Arquillian Drone extension for Arquillian provides a simple way of including functional tests for your web based application. Arquillian Drone manages the life cycle of web testing tool, which is either Arquillian Ajocado, Selenium or WebDriver. Arquillian Drone automatically manages life cycle of objects required for interaction between browser and deployed application.

The following example illustrates how Arquillian Drone can be used with Arquillian Ajocado. This example is a part of Arquillian Drone test classes, so you are free to experiment with it. Arquillian Ajocado is a Selenium on steroids, because it provides type safe API over classic DefaultSelenium object, has extended support for handling AJAX based UI and adds pretty fast JQuery locators to you browser, so your test are executed faster. If you are not experienced with Arquillian Ajocado, you can still use DefaultSelenium or WebDriver specific browser, such as
FirefoxDriver. The beauty of Arquillian Drone is that is supports all of them and their usage is pretty much the same.

package org.jboss.arquillian.drone.example;

import static org.jboss.arquillian.ajocado.Ajocado.elementPresent;
import static org.jboss.arquillian.ajocado.Ajocado.id;
import static org.jboss.arquillian.ajocado.Ajocado.waitForHttp;
import static org.jboss.arquillian.ajocado.Ajocado.waitModel;
import static org.jboss.arquillian.ajocado.Ajocado.xp;

import java.io.File;
import java.net.URL;

import org.jboss.arquillian.ajocado.framework.AjaxSelenium;
import org.jboss.arquillian.ajocado.locator.IdLocator;
import org.jboss.arquillian.ajocado.locator.XPathLocator;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.importer.ZipImporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests Arquillian Drone extension against Weld Login example.
 * Uses Ajocado driver bound to Firefox browser.
 *
 */
@RunWith(Arquillian.class)
public class AjocadoTestCase extends AbstractTestCase
{
   // load ajocado driver
   @Drone
   AjaxSelenium driver;

   // Load context path to the test
   @ArquillianResource
   URL contextPath;

   protected XpathLocator LOGGED_IN = xp("//li[contains(text(),'Welcome')]");
   protected XpathLocator LOGGED_OUT = xp("//li[contains(text(),'Goodbye')]");

   protected IdLocator USERNAME_FIELD = id("loginForm:username");
   protected IdLocator PASSWORD_FIELD = id("loginForm:password");

   protected IdLocator LOGIN_BUTTON = id("loginForm:login");
   protected IdLocator LOGOUT_BUTTON = id("loginForm:logout");

   @Deployment(testable=false)
   public static WebArchive createDeployment()
   {
      return ShrinkWrap.create(WebArchive.class, "weld-login.war")
            .addClasses(Credentials.class, LoggedIn.class, Login.class, User.class, Users.class)
            .addAsWebInfResource(new File("src/test/webapp/WEB-INF/beans.xml"))
            .addAsWebInfResource(new File("src/test/webapp/WEB-INF/faces-config.xml"))
            .addAsWebInfResource(new File("src/test/resources/import.sql"))
            .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"))
            .addAsResource(new File("src/test/resources/META-INF/persistence.xml"), ArchivePaths.create("META-INF/persistence.xml"))
            .setWebXML(new File("src/test/webapp/WEB-INF/web.xml"));
   }

   @Test
   public void testLoginAndLogout()
   {
      driver.open(contextPath);
      waitModel.until(elementPresent.locator(USERNAME_FIELD));
      Assert.assertFalse("User should not be logged in!", driver.isElementPresent(LOGOUT_BUTTON));
      driver.type(USERNAME_FIELD, "demo");
      driver.type(PASSWORD_FIELD, "demo");

      waitHttp(driver).click(LOGIN_BUTTON);
      Assert.assertTrue("User should be logged in!", driver.isElementPresent(LOGGED_IN));

      waitHttp(driver).click(LOGOUT_BUTTON);
      Assert.assertTrue("User should not be logged in!", driver.isElementPresent(LOGGED_OUT));
   }

}

As you can see, execution does not differ from common Arquillian test much. The only requirement is actually running Arquillian in client mode, which is enforced by marking deployment as testable = false or alternatively by @RunAsClient annotation. The other annotations present in the test are used to inject web test framework instance (@Drone) and context path (@ArquillianResource) for deployed archive into your test. Their life cycle is completely managed by Arquillian Drone, as described in extensions.drone.lifecycle. The instance is used in test method to traverse UI of application via Firefox browser, fill user credentials and signing up and out. Test is based on JUnit, but Arquillian Drone, as well as the rest of Arquillian supports TestNG as well.

Supported frameworks and their versions

Framework name and implementation class

Tested version

Additional information

Arquillian Ajocado - AjaxSelenium

1.0.0.CR1

Requires Selenium Server running

Selenium - DefaultSelenium

2.5.0

Requires Selenium Server running

Selenium - Android, Chrome, Firefox, HtmlUnit, InternetExplorer, IPhone Drivers

2.5.0

Selenium Server is not required

This combination matrix (Chrome, Firefox, HtmlUnit) is tested and known to work. However, we expect that all WebDriver interface based browsers will work. Arquillian Drone does not force you to use a specific version of web framework test implementation, so feel free to experiment with it.

Maven setup example

Arquillian Drone requires a few test dependencies which are marked as provided to let you choose their versions. Add following code into your Maven dependecies to enable Arquillian Drone functionality in your test cases.

<properties>
    <version.arquillian.drone>1.0.0.CR2</version.arquillian.drone>
</properties>

<dependencyManagement>
    <dependencies>
        <!-- Arquillian Drone dependencies and Selenium dependencies -->
        <dependency>
            <groupId>org.jboss.arquillian.extension</groupId>
            <artifactId>arquillian-drone-bom</artifactId>
            <version>${version.arquillian.drone}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- Add following dependencies into <dependencies> section -->

<!-- Arquillian Drone support in Arquillian tests -->
<dependency>
    <groupId>org.jboss.arquillian.extension</groupId>
    <artifactId>arquillian-drone-impl</artifactId>
    <scope>test</scope>
</dependency>

<!-- Support for Selenium in Arquillian Drone -->
<dependency>
    <groupId>org.jboss.arquillian.extension</groupId>
    <artifactId>arquillian-drone-selenium</artifactId>
    <scope>test</scope>
</dependency>

<!-- Support for Selenium Server in Arquillian Drone -->
<!-- Required only if you want Arquillian to control Selenium Server life cycle -->
<dependency>
    <groupId>org.jboss.arquillian.extension</groupId>
    <artifactId>arquillian-drone-selenium-server</artifactId>
    <scope>test</scope>
</dependency>

<!-- Selenium -->
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <scope>test</scope>
</dependency>

<!-- Selenium Server -->
<!-- Required only if you want Arquillian to control Selenium Server life cycle -->
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-server</artifactId>
    <scope>test</scope>
</dependency>

<!-- Selenium Server dependency -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId> <!-- choose different underlying implementation if you want -->
    <scope>test</scope> <!-- up to you, tested with 1.5.10 -->
</dependency>

To use WebDriver, simply replace org.jboss.arquillian.extension:arquillian-drone-selenium with org.jboss.arquillian.extension:arquillian-drone-webdriver.

You can combine as many frameworks together as you want. There is no problem to have DefaultSelenium, ChromeDriver and FirefoxDriver in one test suite

To use Arquillian Ajocado, dependencies are already prepared for you. Simply use either JUnit or TestNG artifact, which
includes all Drone and Selenium dependencies:

<dependency>
    <groupId>org.jboss.arquillian.ajocado</groupId>
    <artifactId>arquillian-ajocado-junit</artifactId>
    <type>pom</type>
    <scope>test</scope>
</dependency>

Arquillian Ajocado is not yet updated to Drone 1.0.0.CR2, you can expect it soon to happen.

Life cycle of @Drone objectsArquillian Drone does not allow you to control life cycle of web testing framework objects, but it provides two different scenarios which should be sufficient for most usages required by developers. These are

  1. Class based life cycle

  2. Method based life cycle

For class based life cycle, configuration for the instance is created before a test class is run. This configuration is used to propertly initialize an instance of the tool. The instance is injected into the field and hold until the last test in the test class is
finished, then it is disposed. You can think of @BeforeClass and @AfterClass equivalents. On the other hand, for method based life cycle, an instance is configured and created before Arquillian enters test method and it is disposed after method finishes. You can think of @Before and @After equivalents.

It is import to know that you can combines multiple instances in one tests and you can have them in different scopes. You can as well combine different framework types. Following example shows class based life cycle instance foo of type AjaxSelenium (Arquillian Ajocado) combined with method based life cycle bar of type DefaultSelenium (Selenium).

@RunWith(Arquillian.class)
@RunAsClient
public class EnrichedClass
{
   @Drone AjaxSelenium foo;

   public void testRatherWithSelenium(@Drone DefaultSelenium bar)
   {
     ...
   }
}

Keeping multiple @Drone instances of the same typeWith 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 concept of @Qualifier, which may be known to you from CDI. @Qualifier is a meta-annotations which allows you to annotate annotation you create to tell instances appart. By default, if no
@Qualifier annotation is present, Arquillian Drone uses @Default. Following code defines new qualifying annotation

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 you tests, for example in following way, having two distinct class based life cycle instances of DefaultSelenium.

@RunWith(Arquillian.class)
@RunAsClient
public class EnrichedClass
{
   @Drone DefaultSelenium foo;
   @Drone @Different DefaultSelenium bar;

   public void testWithBothFooAndBar()
   {
     ...
   }
}

Configuring @Drone instances

@Drone instances are automatically configured from arquillian.xml descriptor file, with possibility of overridding arquillian.xml configuration by system properties. Every type of @Drone instance has its own configuration namespace, although namespace overlap in areas where it makes sense, such as sharing a part of configuration between Selenium and Selenium server. System properties always take precedence.

If you are using @Qualifiers, you can use them to modify configuration, such as create a special configuration for a method based life cycle browser.

Arquillian Ajocado configuration

Property name

Default value

Description

contextRoot

http://localhost:8080

Web Server URL

contextPath

(empty)

Application URL of your deployed application

browser

*firefox

Browser type, following Selenium conventions

resourcesDirectory

target/test-classes

Directory where additional resources are stored

buildDirectory

target

Directory where application is built

seleniumHost

localhost

Name of the machine where Selenium server is running

seleniumPort

14444

Port on machine where Selenium server is running

seleniumMaximize

false

Maximize browser window

seleniumDebug

false

Produce debug output in browser console

seleniumNetworkTrafficEnabled

false

Capture network traffic in browser console

seleniumSpeed

0

Delay in ms before each command is sent

seleniumTimeoutDefault

30000

Default timeout in ms

seleniumTimeoutGui

5000

Timeout of GUI wait in ms

seleniumTimeoutAjax

15000

Timeout of AJAX wait in ms

seleniumTimeoutModel

30000

Timout of Model wait in ms

Arquillian Ajocado uses ajacodo namespace. This means, you can define properties either in arquillian.xml

<extension qualifier="ajocado">
   <property name="seleniumHost">myhost.org</property>
</extension>

Or you can convert property name to name of system property, using following formula arqullian. + (namespace) + . + (property name converted to dotted lowercase). For instance, seleniumNetworkTrafficEnabled will be converted to arquillian.ajocado.selenium.network.traffic.enabled System property name.

Selenium configuration

Property name

Default value

Description

serverPort

14444

Port on machine where Selenium server is running

serverHost

localhost

Name of the machine where Selenium server is running

url

http://localhost:8080

Web Server URL

timeout

60000

Default timeout in ms

speed

0

Delay in ms before each command is sent

browser

*firefox

Browser type, following Selenium conventions

Selenium uses selenium namespace.

WebDriver configuration

Property name

Default value

Description

implementationClass

org.openqa.selenium.htmlunit.HtmlUnitDriver

Determines which browser instance is created for WebDriver testing

All WebDriver drivers use webdriver namespace. WebDriver configuration is shared between all instances, the other drivers have more possibilities.

AndroidDriver

Property name

Default value

Description

androidRemoteAddress

 

Default address for remote driver to connect

ChromeDriver

Property name

Default value

Description

chromeBinary

 

Path to chrome binary

chromeDriverBinary

 

Path to chromedriver binary

chromeSwitches

 

List of space separated chrome switches

FirefoxDriver

Property name

Default value

Description

firefoxBinary

 

Path to firefox binary

firefoxProfile

 

Path to Firefox profile

HtmlUnitDriver

Property name

Default value

Description

applicationName

 

Application agent name

applicationVersion

 

Application agent version

userAgent

 

User agent identification

browserVersionNumeric

 

Version of the application agent

useJavaScript

false

Use JavaScript

IPhoneDriver

Property name

Default value

Description

iphoneRemoteAddress

 

Default address for remote driver to connect

InternetExplorerDriver

Property name

Default value

Description

iePort

 

Default port where to connect for Internet Explorer driver

Selenium Server configuration

Property name

Default value

Description

avoidProxy

false

Do not use proxy for connection between clients and server

browserSessionReuse

false

Reuse browser session

browserSideLog

false

Enable logging in browser window

debug

false

Enable debug messages

dontTouchLogging

false

Disable Selenium specific logging configuration

ensureCleanSession

false

Automatic cleanup of the session

firefoxProfileTemplate

 

Path to the profile used as a template

forcedBrowserMode

 

Mimic browser mode no matter which one is used to start the client

honorSystemProxy

false

Use system proxy for connections

host

localhost

Name of the machine where to start Selenium Server

logFile

 

Path to log file

nonProxyHosts

value of http.nonProxyHosts property

List of hosts where proxy settings are ignored

port

14444

Port on machine where to start Selenium Server

profilesLocation

 

Where profiles are located

proxyHost

value of http.proxyHost property

Name of proxy server

proxyInjectionMode

false

Use proxy approach between Selenium server and client

proxyPort

value of http.proxyPort property

Port of proxy server

retryTimeoutInSeconds

10

Timeout for commands to be retried

singleWindow

false

Use single window

skip

false

Do not manage Selenium Server lifecycle

systemProperties

 

Arbitrary system properties in -Dproperty.name=property.value format

timeoutInSeconds

Integer.MAX_VALUE

Timeout for Selenium Server

trustAllSSLCertificates

false

Trust all SSL certificates

trustStore

value of javax.net.ssl.trustStore property

Trust store path

trustStorePassword

value of javax.net.ssl.trustStorePassword property

Trust store password

userExtensions

 

Path to user extension files

Selenium Server uses selenium-server namespace.

Please note that non-letter characters are converted to dots, so for instance to disable Selenium Server via System property, you have to set arquillian.selenium.server.skip to true.

Selenium Server has different life cycle than @Drone instance, it is created and started before test suite and disposed after test suite. If you have your own Selenium Server instance running, you simply omit its configuration, however specifying it is the simplest way how to start it and have it managed by Arquillian.

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 namespace to include - (@Qualifier annotation name converted to lowercase). Please note, that for System properties are all non-letter characters converted to dots. For instance, if you qualified Arquillian Ajocado instance with @MyExtraBrowser, its namespace will become ajocado-myextrabrowser.

The namespace resolution is a bit more complex. Arquillian Drone will search for configuration in following order:

  1. Search for the exact match of namespace (e.g. ajocado-myextrabrowser) in arqullian.xml, if found, step 2 is not performed

  2. Search for a match of base namespace, without qualifier (e.g. ajocado) in arqullian.xml

Then System property overriddes are applied in the same fashion.

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 behaviour of existing implemenation or implement support for your own testing framework. See JavaDoc/sources for more details, here is an enumeration of classes you should focus on:

Implementations of Configurator, Instantiator and Destructor are searched on the class path and they are sorted according to precedence they declare. Arquillian Ajocado default implemenation has precedence of 0, so if your implementation has bigger precedence and instantiates type T with configuration C, Arquillian Drone will use it. 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 manage instance of arbitrary web testing framework.

Arquillian Drone SPI extensions are activated in a LoadableExtension, which is a default extension method in Arquillian.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 12:19:03 UTC, last content change 2012-03-27 02:14:03 UTC.