JBoss.orgCommunity Documentation
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.
You no longer need to manually post processor your WebArchives with JSFUnit dependecies
You can easly test single pages
Both in-contianer and client mode support
Use JUnit 4.8.1 or TestNG 5.12.1
JSFUnit integration requires a Java EE 6 compliant server. The packaging is based on web-fragments from Servlet 3.0.
// imports here...
@RunWith(Arquillian.class)
public class JSFUnitTestCase
{
@Deployment
public static WebArchive createDeployment()
{
return ShrinkWrap.create(WebArchive.class ,"test.war")
.addClasses(
RequestScopeBean.class,
ScopeAwareBean.class)
.setWebXML("jsf/jsf-web.xml")
.addResource("jsf/index.xhtml", "index.xhtml")
.addWebResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"));
}
@Test
public void shouldExecutePage() throws Exception
{
JSFSession jsfSession = new JSFSession("/index.jsf");
Assert.assertTrue(Environment.is12Compatible());
Assert.assertTrue(Environment.is20Compatible());
Assert.assertEquals(2, Environment.getJSFMajorVersion());
Assert.assertEquals(0, Environment.getJSFMinorVersion());
JSFServerSession server = jsfSession.getJSFServerSession();
Assert.assertEquals("request", server.getManagedBeanValue("#{requestBean.scope}"));
}
}
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>
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.
Table 11.1. Supported frameworks and their tested versions
Framework name and implementation class | Tested version | Additional information |
---|---|---|
Arquillian Ajocado - AjaxSelenium | 1.0.0.Alpha1 | Requires Selenium Server running |
Selenium - DefaultSelenium | 2.0b2 | Requires Selenium Server running |
Selenium - HtmlUnitDriver , FirefoxDriver | 2.0b2 | Selenium Server is not required |
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>
Arquillian 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
Class based life cycle
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)
@RunAs(AS_CLIENT)
public class EnrichedClass
{
@Drone AjaxSelenium foo;
public void testRatherWithSelenium(@Drone DefaultSelenium bar)
{
...
}
}
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 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.factory;
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.spi.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)
@RunAs(AS_CLIENT)
public class EnrichedClass
{
@Drone DefaultSelenium foo;
@Drone @Different DefaultSelenium bar;
public void testWithBothFooAndBar()
{
...
}
}
@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.
Table 11.2. 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">
<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.
Table 11.3. 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.
Table 11.4. WebDriver configuration
Property name | Default value | Description |
---|---|---|
implementationClass | org.openqa.selenium.htmlunit.HtmlUnitDriver | Determines which browser instance is created for WebDriver testing |
WebDriver uses webdriver
namespace.
Table 11.5. Selenium Server configuration
Property name | Default value | Description |
---|---|---|
port | 14444 | Port on machine where to start Selenium Server |
host | localhost | Name of the machine where to start Selenium Server |
output | target/selenium-server-output.log | Name of file where to redirrect Selenium Server logger |
enable | false | Enable Arquillian to start Selenium Server |
Selenium Server uses selenium-server
namespace.
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:
Search for the exact match of namespace (e.g.
ajocado-myextrabrowser
) in arqullian.xml, if found, step 2 is not
performed
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:
Provides a way how to configure configurations of type C for
object of type T@Drone
Provides a way how to instantiate @Drone
object of type T
with configuration C
Provides a way how to dispose @Drone
object of type T
This is effectivelly a marker for configuration of type C
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.