JBoss.orgCommunity Documentation

Chapter 11. Complete Extension/Framework Reference

11.1. Performance
11.2. JSFUnit
11.3. Drone

The performance extension to Arquillian is a simple way of checking that the code you want to test performs within the range you want it to. It's can also automatically catch any performance regressions that might be added to your applications. - and as Arquillian itself, its very easy to use.



// include other arquillian imports here...
import org.jboss.arquillian.performance.annotation.Performance;
import org.jboss.arquillian.performance.annotation.PerformanceTest;
@PerformanceTest(resultsThreshold=2)
@RunWith(Arquillian.class)
public class WorkHardCdiTestCase
{
   @Deployment
   public static JavaArchive createDeployment() {
       return ShrinkWrap.create(JavaArchive.class ,"test.jar")
               .addPackage( WorkHard.class.getPackage())
               .addAsManifestResource(
                       EmptyAsset.INSTANCE,
                       ArchivePaths.create("beans.xml"));
   }
   
   @Inject HardWorker worker;
   
   @Test
   @Performance(time=20)
   public void doHardWork() throws Exception 
   {
      Assert.assertEquals(21, worker.workingHard(), 0d);
   }
}
    

As you can see the only two additions needed are @Performance and @PerformanceTest. They do different things and can be used seperately or combined.

@Performance require one argument, time (a double) which set the required maximum time that the test is allowed to spend in milliseconds. If the test exceeds that time it will fail with an exception explaining the cause.

@PerformanceTest will cause every testrun of that test to be saved and every new run will compare results with previous runs. If the new testrun exceeds the previous runs with a defined threshold an exception will be thrown. The threshold can be set with the parameter resultsThreshold. It is by default set to 1d.

How threshold is calculated: resultsThreshold * newTime < oldTime.

The only extra dependency needed is to add arquillian-performance to your pom.xml. Take a look at the Chapter 3, Getting started to see how you set up arquillian using maven.


<dependency>
   <groupId>org.jboss.arquillian.extension</groupId>
   <artifactId>arquillian-performance</artifactId>
   <version>${arquillian.version}</version>
   <scope>test</scope>
</dependency>
    

The JSFUnit integration to Arquillian is a simpler way of using JSFUnit.

The only dependencies needed is to add org.jboss.arquillian.framework:arquillian-framework-jsfunit and org.jboss.jsfunit:jboss-jsfunit-core to your pom.xml. The rest is handled by Arquillian in the background. Take a look at the Chapter 3, Getting started to see how you set up arquillian using maven.


<dependency>
   <groupId>org.jboss.arquillian.framework</groupId>
   <artifactId>arquillian-framework-jsfunit</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.jboss.jsfunit</groupId>
   <artifactId>jboss-jsfunit-core</artifactId>
   <version>1.3.0.Final</version>
   <scope>test</scope>
</dependency>
    

Warning

To use JSFUnit with Arquillian, JSFUnit 1.3.0.Final is required.

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.

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.waitModel;
import static org.jboss.arquillian.ajocado.guard.request.RequestTypeGuardFactory.waitHttp;
import static org.jboss.arquillian.ajocado.locator.LocatorFactory.id;
import static org.jboss.arquillian.ajocado.locator.LocatorFactory.xp;
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.api.Run;
import org.jboss.arquillian.drone.annotation.ContextPath;
import org.jboss.arquillian.drone.annotation.Drone;
import org.jboss.arquillian.junit.Arquillian;
import org.junit.Assert;
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
   @ContextPath
   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 utestable = 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 (@ContextPath) for deployed archive into your test. Their life cycle is completely managed by Arquillian Drone, as described in Section 11.3.3, “Life cycle of @Drone objects”. 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.


This combination matrix 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.

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.


<!-- Arquillian Drone dependency -->
<dependency>
   <groupId>org.jboss.arquillian.extension</groupId>
   <artifactId>arquillian-drone</artifactId>
   <version>${arquillian.version}</version>
   <scope>test</scope>
</dependency>

<!-- Arquillian Ajocado dependencies -->
<dependency>
   <groupId>org.jboss.arquillian.ajocado</groupId>
   <artifactId>arquillian-ajocado-api</artifactId>
   <version>${version.ajocado}</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.jboss.arquillian.ajocado</groupId>
   <artifactId>arquillian-ajocado-impl</artifactId>
   <version>${version.ajocado}</version>
   <scope>test</scope>
</dependency>

<!-- Selenium (including WebDriver in 2.x versions) -->
<dependency>
   <groupId>org.seleniumhq.selenium</groupId>
   <artifactId>selenium-remote-control</artifactId>
   <version>${version.selenium}</version>
   <scope>test</scope>        
</dependency>
<dependency>
   <groupId>org.seleniumhq.selenium</groupId>
   <artifactId>selenium-server</artifactId>
   <version>${version.selenium}</version>
   <scope>test</scope>
</dependency>
      
<!-- required to run Selenium Server, needed if you want Arquillian Drone to start Selenium Server for you -->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-simple</artifactId>  <!-- choose different underlying implementation if you want -->
   <version>${version.slf4j}</version>  <!-- up to you, tested with 1.5.10 -->
   <scope>test</scope>
</dependency>

@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 uses ajacodo namespace. This means, you can define properties either in arquillian.xml


<extension qualifier="ajocado">
   <configuration>
      <property name="seleniumHost">myhost.org</property>
   </configuration>
</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 uses selenium namespace.


WebDriver uses webdriver namespace.


Selenium Server uses selenium-server namespace.

Warning

Please note that non-letter characters are converted to dots, so for instance to enable Selenium via System property, you have to set arquillian.selenium.server.enable 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.

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.

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:

The import note is that implemenation 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 searched via descriptions in META-INF/services on class path. For instance, to override DefaultSelenium instantiator, create file META-INF/services/org.jboss.arquillian.drone.spi.Instantiator with following content:

fully.quallified.name.of.my.implementation.Foo

Your class Foo must implement Instantiator<DefaultSelenium,SeleniumConfiguration> interface.