1. Arquillian makes integration testing a breeze!
Picking up where unit tests leave off, Arquillian handles all the plumbing of container management, deployment and framework initialization so you can focus on the task at hand, writing your tests. Real tests. In short…
Arquillian brings the test to the runtime so you don’t have to manage the runtime from the test (or the build). Arquillian eliminates this burden by covering all aspects of test execution, which entails:
-
Managing the lifecycle of the container (or containers)
-
Bundling the test case, dependent classes and resources into a ShrinkWrap archive (or archives)
-
Deploying the archive (or archives) to the container (or containers)
-
Enriching the test case by providing dependency injection and other declarative services
-
Executing the tests inside (or against) the container
-
Capturing the results and returning them to the test runner for reporting
To avoid introducing unnecessary complexity into the developer’s build environment, Arquillian integrates seamlessly with familiar testing frameworks (e.g., JUnit 4, TestNG 5), allowing tests to be launched using existing IDE, Ant and Maven test plugins — without any add-ons.
2. Guide
2.1. Introduction
We believe that integration and functional testing should be no more complex or daunting than unit testing. We created Arquillian to make that vision a reality.
Arquillian is a revolutionary testing platform for Java and the JVM that enables developers to easily write and execute integration and functional tests for Java middleware, ranging from Java EE and beyond.
Arquillian picks up where unit tests leave off, focusing on the integration of application code inside a real runtime environment. Much the same way Java EE 5 simplified the server programming model by providing declarative services for POJOs, Arquillian equips tests with container lifecycle management and test case enrichment. Using Arquillian, you can focus on the test, not the plumbing.
In short, Arquillian aims to make integration and functional testing a breeze.
2.1.1. Core Principles
The Arquillian project adheres to three core principles:
-
Tests should be portable to any supported container
By keeping container-specific APIs out of the tests, developers can verify application portability by running tests against a variety of containers. It also means that lightweight containers can be used as a substitute for full containers during development. -
Tests should be executable from both the IDE and the build tool
By leveraging the IDE, the developer can skip the build for a faster turnaround and has a familiar environment for debugging. These benefits shouldn’t sacrifice the ability to run the tests in continuous integration using a build tool. -
The platform should extend or integrate existing test frameworks
An extensible architecture encourages reuse of existing software and fosters a unified Java testing ecosystem.
Arquillian brings your test to the runtime so you don’t have to manage the runtime from the test. Arquillian packs the necessary orchestration logic neatly into the platform an its extensions. As a result, executing an Arquillian test is as simple as selecting "Run As > Test" in the IDE or executing the "test" goal from the build tool.
2.1.2. How It Works
Using Arquillian, you write a basic test case and annotate it with declarative behavior that says, "`@RunWith` Arquillian". This declaration tells Arquillian to take over execution of the test when it’s launched.
That’s when the real magic happens.
Launching an Arquillian test is as simple as right-clicking the test
class in the IDE and selecting Run As > Test (e.g., JUnit, TestNG,
Spock, etc). Based on the classpath configuration, Arquillian starts or
binds to the target container (JBoss AS, GlassFish, OpenEJB, etc) and
deploys the test archive defined in the @Deployment
method. The
archive includes the test case along with the specified classes,
resources and libraries. Your test then executes inside the container
and enjoys all the same services as an application component. That means
you get dependency and resource injection into the test, you can access
EJBs, you can load a persistence unit, you can get a handle to a
database connection, etc. Arquillian then captures the test results and
transports them back to the test runner for reporting.
Arquillian also has a client run mode, which only deploys the test archive, not the test case. |
Aside from a few extra declarations (i.e., @RunWith
and
@Deployment
), an Arquillian test looks like any other unit test and
launched like any other unit test.
2.1.3. Integration Testing In Java EE
Integration testing is very important in Java EE. The reason is two-fold:
-
Business components often interact with resources or sub-system provided by the container
-
Many declarative services get applied to the business component at runtime
The first reason is inherent in enterprise applications. For the application to perform any sort of meaningful work, it has to pull the strings on other components, resources (e.g., a database) or systems (e.g., a web service). Having to write any sort of test that requires an enterprise resource (database connection, entity manager, transaction, injection, etc) is a non-starter because the developer has no idea what to even use. Clearly there is a need for a simple solution, and Arquillian fills that void.
Some might argue that, as of Java EE 5, the business logic performed by most Java EE components can now be tested outside of the container because they are POJOs. But let’s not forget that in order to isolate the business logic in Java EE components from infrastructure services (transactions, security, etc), many of those services were pushed into declarative programming constructs. At some point you want to make sure that the infrastructure services are applied correctly and that the business logic functions properly within that context, justifying the second reason that integration testing is important in Java EE.
Testing The Real Component
…or, what you test is what you run.
The reality is that you aren’t really testing your component until you test it in situ. It’s all to easy to create a test that puts on a good show but doesn’t provide any real guarantee that the code under test functions properly in a production environment. The show typically involves mock components and/or bootstrapped environments that cater to the test. Such "unit tests" can’t verify that the declarative services kick in as they should. While unit tests certainly have value in quickly testing algorithms and business calculations within methods, there still need to be tests that exercise the component as a complete service.
Rather than instantiating component classes in the test using Java’s new operator, which is customary in a unit test, Arquillian allows you to inject the container-managed instance of the component directly into your test class (or you can look it up in JNDI) so that you are testing the actual component, just as it runs inside the application.
Finding A Happy Medium
Do you really need to run the test in a real container when a Java SE CDI environment would do?
It’s true, some tests can work without a full container. For instance, you can run certain tests in a Java SE CDI environment with Arquillian. Let’s call these "standalone" tests, whereas tests which do require a full container are called "integration" tests. Every standalone test can also be run as an integration test, but not the other way around. While the standalone tests don’t need a full container, it’s also important to run them as integration tests as a final check just to make sure that there is nothing they conflict with (or have side effects) when run in a real container.
It might be a good strategy to make as many tests work in standalone mode as possible to ensure a quick test run, but ultimately you should consider running all of your tests in the target container. As a result, you’ll likely enjoy a more robust code base.
We’ve established that integration testing is important, but how can integration testing being accomplished without involving every class in the application? That’s the benefit that ShrinkWrap brings to Arquillian.
Controlling The Test Classpath
One huge advantage ShrinkWrap brings to Arquillian is classpath control. The classpath of a test run has traditionally been a kitchen sink of all production classes and resources with the test classes and resources layered on top. This can make the test run indeterministic, or it can just be hard to isolate test resources from the main resources.
Arquillian uses ShrinkWrap to create "micro deployments" for each test, giving you fine-grained control over what you are testing and what resources are available at the time the test is executed. An archive can include classes, resources and libraries. This not only frees you from the classpath hell that typically haunts test runners (Eclipse, Maven), it also gives you the option to focus on the interaction between an subset of production classes, or to easily swap in alternative classes. Within that grouping you get the self-assembly of services provided by Java EE---the very integration which is being tested.
Let’s move on and consider some typical usage scenarios for Arquillian.
2.2. Getting Started
We promised that integration and functional testing with Arquillian is no more complex than writing a unit test. Now it’s time to prove it to you. We’ve prepared a series of guides designed exclusively to teach you how to use Arquillian. You’ll have the tests running from both Maven and Eclipse by the end of the first guide.
Head over to the Getting Started Guide and start writing real tests!
2.3. Deployment Archives
Each Arquillian test is associated with at least one deployment. The
deployment is configured using a static method annotated with
@Deployment
that returns a ShrinkWrap archive. Here’s an example:
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(Greeter.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
Why is a deployment necessary?
If we want to be able to test inside the container, we need some way to get the code into the container. A container typically accepts some form of deployment. In most Java-based containers, these deployments are some sort of Java archive (jar).
ShrinkWrap provides a way to define a deployment archive (e.g., jar, war, ear) in Java using a fluent API. You use this API to create an deployment that’s associated with the test case, which we call a "micro deployment". Arquillian can then use this object to stream the contents to the server in the most efficient way available.
The deployment archive is more than just a means to an end (getting code inside the container). There are several critical benefits to defining a deployment:
-
It allows you to isolate the classpath of the test – only those classes and resources inside the deployment are visible to the test.
-
You can emulate or replicate a deployment scenario, which often triggers behavior (e.g., framework activation) that you cannot otherwise test.
-
We can skip the build. Since the archive is assembled in Java, we’re able to leverage the incremental compilation provided by IDEs.
In summary, the strategy of using ShrinkWrap-based deployments gives you tremendous control over the code you’re testing and quicker turnaround.
Arquillian provides the SPI
org.jboss.arquillian.spi.client.deployment.DeploymentScenarioGenerator
to allow an alternate means of defining a deployment, including
foregoing the use of a deployment altogether.
|
2.4. Deployment Archives using Java SPI
In Deployment Archives you’ve read how to create an archive to deploy into container using @Deployment
annotation.
But you can implement your own method to generate the archive to be deployed using a Java SPI org.jboss.arquillian.container.test.spi.client.deployment.AutomaticDeployment
.
The service must conform next signature:
/**
* Method called for generating the deployment configuration.
* @param testClass of current running test.
* @return Model object that contains all the information related to deployment configuration.
*/
DeploymentConfiguration generateDeploymentScenario(TestClass testClass);
To build DeploymentConfiguration type you can use org.jboss.arquillian.container.test.api.DeploymentConfiguration.DeploymentContentBuilder class.
|
Remember to register the SPI by creating META-INF/services/org.jboss.arquillian.container.test.spi.client.deployment.AutomaticDeployment file containing the fully qualified name of your implementation.
|
Moreover, you can use @BeforeDeployment
method annotation which allows you to modify the archive before it is deployed.
The method must be public static and receive as a parameter an org.jboss.shrinkwrap.api.Archive
which is the archive created by AutomaticDeployment
implementation and return a modified org.jboss.shrinkwrap.api.Archive
.
@BeforeDeployment
public static Archive addDeploymentContent(Archive archive) {
// Modify Archive
return archive;
}
2.5. Containers
Arquillian’s forte is not only its ease of use, but also its extensibility. Good integration testing is not just about testing in any environment, but rather testing in the environment of your application targets. It’s easy to give ourselves false assurance by validating components in a specialized testing container, or by using mocks, only to realize that small variations and assumptions cause the components to fail when the application is deployed to production. To make tests count, you want to execute them in the target environment, or container.
So what is the container that Arquillian uses? Is it some proprietary testing container that emulates the behavior of the technology (Java EE)?
Nope! It’s pluggable. It can be your target runtime, such as JBoss AS, GlassFish or Tomcat. Or it can even been an embedded container such as OpenEJB, GlassFish Embedded or Weld SE. You can even use one container for development and another for continuous integration.
This portability is made possible by a RPC-style (or local, if applicable) communication between the test runner and the container, a real container. You can run the same test case against various containers and you don’t get locked into a proprietary test environment.
Arquillian recognizes three container interaction styles:
-
A remote container resides in a separate JVM from the test runner. Arquillian binds to the container to deploy the test archive and invokes tests via a remote protocol (e.g., Servlet, JMX).
-
A managed container is similar to a remote container, except its lifecycle (startup/shutdown) is also managed by Arquillian.
-
An embedded container resides in the same JVM and is mostly likely managed by Arquillian. Tests are executed via a local protocol for containers without a web component and via a remote protocol for containers with a web component. No need to fiddle with those Maven plugins!
A container is further classified by its capabilities:
-
Java EE application server (e.g., JBoss AS, GlassFish, WebLogic)
-
Servlet container (e.g., Tomcat, Jetty)
-
standalone bean container (e.g., OpenEJB, Weld SE)
-
OSGi container
Arquillian can control a variety of containers out of the box. If the container you are using isn’t supported, Arquillian provides an SPI that allows you to introduce any additional container to the default collection.
2.5.1. Container Selection
How can a single Arquillian test be executed in different containers? How does Arquillian decide which container to target? And how are both remote and local invocations supported?
The answers to these questions touch upon the extensibility of Arquillian.
Container selection occurs at runtime. Arquillian selects the target container according to which container adapter is available on the runtime classpath at the time the test is launched. Therefore, you must include the container adapter as a test-scoped dependency that corresponds to the container you want to target.
Arquillian delegates to an SPI (Service Provider Interface) to handle
starting and stopping the server and deploying and undeploying archives.
In this case, the SPI is the interface
org.jboss.arquillian.spi.client.DeployableContainer
. If you recall
from the getting started guide, we added an Arquillian container adapter
according to the target container we wanted to use. That library
contains an implementation of this interface, thus controlling how
Arquillian handles deployment.
If you want to add support for another container in Arquillian, you need
to provide an implementation of the DeployableContainer
interface.
To switch to another container, you just change which container adapter is on the classpath before running the test.
You can only have one container adapter on the classpath at a time. The execution of the test will be aborted if more than one adapter is detected. |
One way to swap the libraries on the classpath is to manually edit the dependencies defined in the pom.xml each time. But that’s just tedious. The recommended approach is to use Maven profiles.
Maven profiles allow you to partition your dependencies into groups, one group for each container adapter and its related artifacts. When running the tests, you activate one of those groups, which in turn selects a target container. The profile is activated either using either a commandline flag (-P) or a preference in the IDE.
The Getting Started Guide explains how to setup and use Maven profiles for this purpose in more detail.
So far we’ve covered how a container adapter is selected. It’s also possible to configure the container adapter, which we’ll look at next.
2.5.2. Container Configuration
We’ve covered how a container gets selected, but how does Arquillian know how to locate or communicate with the container? That’s where configuration comes in.
You can come a long way with default values, but at some point you may need to customize some of the container settings to fit your environment. Let’s see how this can be done with Arquillian.
Arquillian will look for configuration settings in a file named
arquillian.xml
in the root of your classpath. If it exists it will be
auto loaded, else default values will be used. This file is not a
requirement.
Let’s imagine that we’re working for the company example.com
and in
our environment we have two servers; test.example.com
and
hudson.example.com
. test.example.com
is the JBoss instance we use
for our integration tests and hudson.example.com
is our continuous
integration server that we want to run our integration suite from. By
default, Arquillian will use localhost, so we need to tell it to use
test.example.com
to run the tests. The JBoss AS container by default
use the Servlet protocol, so we have to override the default
configuration.
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://www.jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="jbossas" default="true">
<configuration>
<property name="providerUrl">jnp://test.example.com:1099</property>
</configuration>
<protocol type="Servlet 3.0">
<property name="host">test.example.com</property>
<property name="port">8181</property>
</protocol>
</container>
</arquillian>
That should do it! Here we use the JBoss AS 6.0 Remote container which
default use the Servlet 3.0 protocol implementation. We override the
default Servlet configuration to say that the http requests for this
container can be executed over test.example.com:8181
, but we also need
to configure the container so it knows where to deploy our archives. We
could for example have configured the Servlet protocol to communicate
with a Apache server in front of the JBoss AS Server if we wanted to.
Each container has different configuration options.
For a complete overview of all the containers and their configuration options, see the container adapters appendix.
2.5.3. Supported Containers
Please see the container adapters appendix for a list of supported containers, including details for how to use and configure them.
2.5.4. Container Config Runtime Selection
During execution of your Arquillian test-suite, you may have configured one or more containers with which you need to execute the test suite. This is usually configured with maven profiles, but in the case where individual container options need to be specified at runtime to the target container. This is where you will need to specify configurations in your arquillian.xml file, and also activate those configurations using Java system properties.
Example arquillian.xml with two container configurations:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="jbossas_managed" default="true">
<protocol type="Servlet 3.0">
<property name="executionType">MANAGED</property>
</protocol>
<configuration>
<property name="jbossHome">${project.baseDir}/target/jboss-as-7.1.1.Final/</property>
<property name="allowConnectingToRunningServer">true</property>
</configuration>
</container>
<container qualifier="jetty">
<configuration>
<more configuration>...</more configuration>
</configuration>
</container>
</arquillian>
Activating a configuration via the command line
The -Darquillian.launch system property is what controls arquillian.xml configuration selection. If you are running tests from Eclipse or directly from the command like, you should add the -D system property to your launch configuration or command.
Activating a configuration via Maven
These configurations may be activated in the maven profile using the Surefire plugin configuration in your container’s maven profile to set the 'arquillian.launch' system property for test execution, as follows:
<profile>
<id>JBOSS_AS_MANAGED_7.X</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<arquillian.launch>jbossas_managed</arquillian.launch>
</systemPropertyVariables>
</configuration>
</plugin>
...
2.6. Test Enrichers
When you use a unit testing framework like JUnit or TestNG, your test case lives in a world on its own. That makes integration testing pretty difficult because it means the environment in which the business logic executes must be self-contained within the scope of the test case (whether at the suite, class or method level). The bonus of setting up this environment in the test falls on the developer’s shoulders.
With Arquillian, you no longer have to worry about setting up the execution environment because that is all handled for you. The test will either be running in a container or a local CDI environment. But you still need some way to hook your test into this environment.
A key part of in-container integration testing is getting access to the container-managed components that you plan to test. Using the Java’s new operator to instantiate the business class is not suitable in this testing scenario because it leaves out the declaratives services that get applied to the component at runtime. We want the real deal. Arquillian uses test enrichment to give us access to the real deal. The visible result of test enrichment is injection of container resources and beans directly into the test class.
2.6.1. Dependency Injection
Before Arquillian negotiates the execution of the test, it enriches the test class by satisfying injection points specified declaratively using annotations.
Test Case Injection Points
Injection points are recognized in the following two locations:
-
Field (any visibility)
-
@Test
method argument
Here’s an example of a field injection in a test class:
@RunWith(Arquillian.class)
public class GreeterTestCase {
@Inject
private Greeter greeter;
...
}
Here’s an example of a method argument injection in a test method:
@Test
public void testGreeting(Greeter greeter) {
...
}
Note that the @Inject
annotation is not required when injecting into a
method argument.
Default Enrichers
There are three injection-based enrichers provided by Arquillian out of the box:
-
@Resource
- Java EE resource injections -
@EJB
- EJB session bean reference injections -
@Inject
,@Resource
,@EJB
,@PersistenceContext
and@PersistenceUnit
- CDI-supported injections
The first two enrichers use JNDI to lookup the instance to inject. The
CDI injections are handled by treating the test class as a bean capable
of receiving standard CDI injections (non-contextual injection). Since
CDI requires containers to satisfy all Java EE injection points on a CDI
bean, the @Resource
, @EJB
, @PersistenceContext
and
@PersistenceUnit
injections are supported transitively by the CDI
enricher. In fact, because of CDI’s tight integration with the
container, @EJB
injections in the test class are satisfied according
to specification when the CDI enricher is available.
Resource Injection
The @Resource
annotation gives you access to any object which is
available via JNDI. It follows the standard rules for @Resource
(as
defined in the Section 2.3 of the Common Annotations for the Java
Platform specification).
EJB Injection (without CDI support)
The @EJB
annotation performs a JNDI lookup for the EJB session bean
reference using the following equation in the specified order:
"java:global/test.ear/test/" + fieldType.getSimpleName() + "Bean",
"java:global/test.ear/test/" + fieldType.getSimpleName(),
"java:global/test/" + fieldType.getSimpleName(),
"java:global/test/" + fieldType.getSimpleName() + "Bean",
"java:global/test/" + fieldType.getSimpleName() + "/no-interface",
"test/" + unqualified interface name + "Bean/local",
"test/" + unqualified interface name + "Bean/remote",
"test/" + unqualified interface name + "/no-interface",
unqualified interface name + "Bean/local",
unqualified interface name + "Bean/remote",
unqualified interface name + "/no-interface"
If no matching beans were found in those locations the injection will fail.
At the moment, the lookup for an EJB session reference relies on some common naming convention of EJB beans. In the future the lookup will rely on the standard JNDI naming conventions established in Java EE 6.
CDI Injection
In order for CDI injections to work, the test archive defined with
ShrinkWrap must be in a bean archive. That means adding beans.xml to the
META-INF directory. Here’s a @Deployment
method that shows one way to
add a beans.xml to the archive:
@Deployment
public static JavaArchive createTestArchive() {
return ShrinkWrap.create(JavaArchive.class, "test.jar")
.addClass(NameOfClassUnderTest.class)
.addAsManifestResource(EmptyAsset.INSTANCE, Paths.create("beans.xml"))
In an application that takes full advantage of CDI, you can likely get
by only using injections defined with the @Inject
annotation.
Regardless, the other two types of injection come in handy from
time-to-time.
2.6.2. Active Scopes
When running your tests the embedded Weld EE container, Arquillian activates scopes as follows:
-
Application scope - Active for all methods in a test class
-
Session scope - Active for all methods in a test class
-
Request scope - Active for a single test method
Scope control is experimental at this point and may be altered in a future release of Arquillian.
2.7. Rules
If you want to use Arquillian Testing Platform with other JUnit Runner, you can now use JUnit Rules - ArquillianTestClass
and ArquillianTest
- and happily let Arquillian do the heavy lifting for your tests.
To get the similar functionality what the @RunWith(Arquillian.class)
is offering you should require both rules i.e. ArquillianTestClass
and ArquillianTest
.
If you are trying to run tests with one rule, it won’t work as per expectations. We are not enforcing user to define both rules as implementations of MethodRuleChain
is different for different users. JUnit doesn’t provide MethodRuleChain
similar to the RuleChain
, as we don’t know your implementation of MethodRuleChain
,
we can’t check if your MethodRuleChain has ArquillianTest
Rule defined.
In order to use this feature, your project needs JUnit 4.12 version.
|
2.7.1. How to use it?
public class GreeterManagedBeanWithJUnitRulesTestCase {
@ClassRule
public static ArquillianTestClass arquillianTestClass = new ArquillianTestClass();
@Rule
public ArquillianTest arquillianTest = new ArquillianTest();
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(Greeter.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Inject
Greeter greeter;
@Test
public void should_greet_earthlings() throws Exception {
String name = "Earthlings";
Assert.assertEquals("Hello, " + name, greeter.greet(name));
}
}
However while using above Junit Rules you can’t inject method scoped Arquillian resources.
Unfortunately it is not possible to have one rule acting as both test class and test method rule. For more info
see this comment.
In order to use any other JUnit Method Rule with ArquillianTest Rule, you have to use it with RuleChain
having ArquillianTest as outer Rule.
e.g.
|
@Rule
public MethodRule testWatchman = MethodRuleChain.outer(new ArquillianTest().around(new TestWatchman());
2.8. Additional Features
This chapter walks through the following additional features that Arquillian provides to address more advanced use cases. These features may even allow you to write tests for scenarios you previously classified as too difficult to test.
2.8.1. Test Run Modes
So far, we’ve focused on testing your application internals, but we also want to test how others (people, or other programs) interact with the application. Typically, you want to make sure that every use case and execution path is fully tested. Third parties can interact with your application in a number of ways, for example web services, remote EJBs or via HTTP. You need to check that your object serialization or networking work for instance.
This is why Arquillian comes with two run modes, in container
and
as client
. in container
is to test your application internals and
as client
is to test how your application is used by clients. Let’s
dive a bit deeper into the differences between the run modes and see how
they affect your test execution and packaging.
Container mode: @Deployment(testable = true)
As we mentioned above, we need to repackage your @Deployment
, adding
some Arquillian support classes, to run in-container. This gives us the
ability to communicate with the test, enrich the test and run the test
remotely. In this mode, the test executes in the remote container.
Arquillian uses this mode by default.
See the Complete Protocol Reference for an overview of the expected
output of the packaging process when you provide a @Deployment
.
Client mode: @Deployment(testable = false)
Now this mode is the easy part. As opposed to in-container mode which
repackages and overrides the test execution, the as-client mode does as
little as possible. It does not repackage your @Deployment
nor does it
forward the test execution to a remote server. Your test case is running
in your JVM as expected and you’re free to test the container from the
outside, as your clients see it. The only thing Arquillian does is to
control the lifecycle of your @Deployment
.
Here is an example calling a Servlet using the as client
mode.
@RunWith(Arquillian.class)
public class LocalRunServletTestCase {
@Deployment(testable = false)
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.addClass(TestServlet.class);
}
@Test
public void shouldBeAbleToCallServlet(@ArquillianResource(TestServlet.class) URL baseUrl) throws Exception {
String body = readAllAndClose(new URL(baseUrl, "/Test").openStream());
Assert.assertEquals(
"Verify that the servlet was deployed and returns the expected result",
"hello",
body);
}
}
Mixed mode
It is also possible to mix the two run modes within the same test class.
If you have defined the Deployment
to be testable, you can specify the
@Test
method to use run mode as client
by using the @RunAsClient
annotation. This will allow two methods within the same test class to run
in different modes. This can be useful if, in as client
mode, you
want to execute against a remote endpoint in your application, and then
in the next test method, use in container
mode to verify some state
the previous remote invocation created on the server side.
@Deployment(testable = true)
public static WebArchive create() {
...
}
@Test // runs in container
public void shouldBeAbleToRunOnContainerSide() throws Exception {
...
}
@Test @RunAsClient // runs as client
public void shouldBeAbleToRunOnClientSide() throws Exception {
...
}
The effect of the different run modes depends on the
DeployableContainer
used. Both modes might seem to behave the same in
some Embedded containers, but you should avoid mixing your internal and
external tests. One thing is that they should test different aspects of
your application and different use-cases, another is that you will miss
the benefits of switching DeployableContainers
and run the same tests
suite against a remote server if you do.
2.8.2. Descriptor Deployment
We have previously seen Arquillian deploys ShrinkWrap Archives, but some
times you need to deploy other items like a JMS Queue or a DataSource
for your test to run. This can be done by using a ShrinkWrap sub project
called ShrinkWrap Descriptors. Just like you would deploy an Archive
you can deploy a Descriptor
.
@Deployment(order = 1)
public static Descriptor createDep1()
{
return Descriptors.create(DataSourceDescriptor.class);
}
@Deployment(order = 2)
public static WebArchive createDep2() {}
@Test
public void testDataBase() {}
2.8.3. Arquillian Resource Injection
When dealing with multiple different environments and hidden dynamic
container configuration you very soon come to a point where you need
access to the backing containers ip/port/context information. This is
especially useful when doing remote end point testing. So instead of
trying to setup all containers on the same ip/port/context, or hard code
this in your test, Arquillian provides something we call
@ArquillianResource
injection. Via this injection point we can expose
multiple internal objects.
Note that @ArquillianResource injection of URLs is only supported
for in-container tests since Arquillian version 1.1.9.Final
(see https://issues.jboss.org/browse/ARQ-540[ARQ-540]).
|
When you need to get a hold of the request URI (up through the context
path) your Deployment
defined, e.g. "http://localhost:8080/test", you
can use @ArquillianResource
on a field or method argument of type URL:
@ArquillianResource
private URL baseURL;
@Test
private void shouldDoX(@ArquillianResource URL baseURL)
{
}
If you are deploying an EAR with multiple WARs, and you’ve deployed a given servlet to just one of them, you can provide the servlet’s class to get the request URI up through the context path for the WAR that contains that servlet, e.g. "http://localhost:8080/test1" vs. "http://localhost:8080/test2":
@ArquillianResource(MyServlet.class)
private URL baseURL;
@Test
private void shouldDoX(@ArquillianResource(MyServlet.class) URL baseURL)
{
}
Note that this version does not return the request URI to the given servlet, e.g. "http://localhost:8080/test2/MyServlet", but again just the request URI up through the context path. |
2.8.4. Multiple Deployments
Sometimes a single Deployment
is not enough, and you need to specify
more than one to get your test done.
Maybe you want to test communication between two different web applications?
Arquillian supports this as well. Simply just add more @Deployment
methods
to the test class and you’re done. You can use the @Deployment.order
if they
need to be deployed in a specific order. When dealing with multiple in
container
deployments, you need to specify which Deployment
context the
individual test methods should run in. You do this by adding a name to
the deployment by using the @Deployment.name
and refer to that name on
the test method by adding @OperateOnDeployment("deploymentName")
.
@Deployment(name = "dep1", order = 1)
public static WebArchive createDep1() {}
@Deployment(name = "dep2", order = 2)
public static WebArchive createDep2() {}
@Test @OperateOnDeployment("dep1")
public void testRunningInDep1() {}
@Test @OperateOnDeployment("dep2")
public void testRunningInDep2() {}
2.8.5. Multiple Containers
There are times when you need to involve multiple containers in the same
test case, if you for instance want to test clustering. The first step
you need to take is to add a group
with multiple containers to your
Arquillian configuration.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<group qualifier="tomcat-cluster" default="true">
<container qualifier="container-1" default="true">
<configuration>
<property name="tomcatHome">target/tomcat-embedded-6-standby</property>
<property name="workDir">work</property>
<property name="bindHttpPort">8880</property>
<property name="unpackArchive">true</property>
</configuration>
</container>
<container qualifier="container-2">
<configuration>
<property name="tomcatHome">target/tomcat-embedded-6-active-1</property>
<property name="workDir">work</property>
<property name="bindHttpPort">8881</property>
<property name="unpackArchive">true</property>
</configuration>
</container>
</group>
</arquillian>
So what we have done here is to say we have two containers that
Arquillian will control, container-1 and container-2. Arquillian will
now instead of starting up one container, which is normal, start up two.
In your test class you can target different deployments against the
different containers using the @TargetsContainer("containerName")
annotation on your Deployment
methods.
@Deployment(name = "dep1") @TargetsContainer("container-1")
public static WebArchive createDep1() {}
@Deployment(name = "dep2") @TargetsContainer("container-2")
public static WebArchive createDep2() {}
@Test @OperateOnDeployment("dep1")
public void testRunningInDep1() {}
@Test @OperateOnDeployment("dep2")
public void testRunningInDep2() {}
We now have a single test class that will be executed in two different
containers. testRunningInDep1
will operate in the context of the
dep1
deployment which is deployed on the container named container-1
and testRunningInDep2
will operate in the context of deployment dep2
which is deployed on container container-2
. As the test moves along,
each method is executed inside the individual containers.
Arquillian does not support ClassLoader isolation on the client side so for this feature to work the container adapter must support running multiple instances within the same ClassLoader/JVM. Currently this only works with containers of type Remote or Managed as the adapter normally will connect to an isolated server started in its own JVM.
2.8.6. Protocol Selection
A protocol is how Arquillian talks and executes the tests inside the
container. For ease of development and configuration a container defines
a default protocol that will be used if no other is specified. You can
override this default behavior by defining the @OverProtocol
annotation on your @Deployment
method.
@Deployment @OverProtocol("MyCustomProtocol")
public static WebArchive createDep1() {}
@Test
public void testExecutedUsingCustomProtocol() {}
When testExecutedUsingCustomProtocol
is executed, instead of using the
containers protocol which is defined by default, Arquillian will use
MyCustomProtocol
to communicate with the container. Since this is
defined on Deployment
level, you can have different test methods which
operate on different deployments and therefore being executed using
different protocols. This can be useful when for instance a protocols
packaging requirements hinder how you define your archive, or you simply
can not communicate with the container using the default protocol due to
e.g. firewall settings.
Arquillian only supports Servlet 2.5 and Servlet 3.0 at this time. EJB 3.0 and 3.1 are planned. But you might implement your own Protocol. For doing this, please see the Complete Protocol Reference for the better knowing what is currently supported.
2.8.7. Enabling Assertions
The first time you try Arquillian, you may find that assertions that use the Java assert keyword are not working. Keep in mind that the test is not executing the same JVM as the test runner.
In order for the Java keyword "assert" to work you have to enable assertions (using the -ea flag) in the JVM that is running the container. You may want to consider specifying the package names of your test classes to avoid assertions to be enabled throughout the container’s source code.
Enabling Assertions In JBoss AS
If you are using JBoss AS, the quickest way to setup debug mode is to add the following line to the end of $JBOSS_AS_HOME/bin/run.conf (Unix/Linux):
JAVA_OPTS="$JAVA_OPTS -ea"
or before the line :JAVA_OPTS_SET in $JBOSS_AS_HOME/bin/run.conf.bat (Windows)
set "JAVA_OPTS=%JAVA_OPTS% -ea"
Keep in mind your container will always run with assertions enabled after making this change. You might want to consider putting some logic in the run.conf* file.
As an alternative, we recommend using the 'Assert' object that comes with your test framework instead to avoid the whole issue. Also keep in mind that if you use System.out.println statements, the output is going to show up in the log file of the container rather than in the test output.
2.9. Protocols
In order to support running the same test locally and in remote containers, Arquillian uses an SPI that controls which protocol is used to negotiate execution of the test. This appendix details the default protocols that Arquillian provides, when they are used and how they can be configured.
2.9.1. Local
The Local Protocol implementation is used by most EE5 compliant
containers. It does nothing to the deployment. The Local Protocol is also
used when executing in run mode as client
.
Packaging rules
@Deployment | Output | Action |
---|---|---|
JavaArchive |
JavaArchive |
Does nothing. |
WebArchive |
WebArchive |
Does nothing. |
EnterpriseArchive |
EnterpriseArchive |
Does nothing. |
2.9.2. Servlet 2.5
The Servlet 2.5 Protocol implementation is used by most EE5 compliant containers. It will attempt to add a war to the deployment.
Packaging rules
@Deployment | Output | Action |
---|---|---|
JavaArchive |
EnterpriseArchive |
Create a new |
WebArchive |
WebArchive |
If a web.xml is found, a Servlet will be added, else a web.xml will be
added. The Servlet |
EnterpriseArchive |
EnterpriseArchive |
Same as |
2.9.3. Servlet 3.0
The Servlet 3.0 Protocol implementation is used by most EE6 compliant containers. It will attempt to add a web-fragment to the deployment.
Packaging rules
@Deployment | Output | Action |
---|---|---|
JavaArchive |
WebArchive |
Creates a new |
WebArchive |
WebArchive |
Adds |
EnterpriseArchive |
EnterpriseArchive |
If a single |
2.10. Build Integration
Just because the Arquillian project uses Maven doesn’t mean you have to use it to run your Arquillian tests. Arquillian is designed to have seamless integration with JUnit and TestNG without any necessary test framework configuration. That means you can use any build system that has a JUnit or TestNG task to execute your Arquillian test cases. Since most of this guide focuses on using Arquillian in a Maven build, this chapter is going to be about alternative build systems, namely Gradle and Ant.
2.10.1. Active Build Ingredient
The secret ingredient required to activate the Arquillian test runner is getting the correct libraries on the classpath. (Often easier said than done). The libraries consist of the Arquillian container integration and the container runtime (for an embedded container) or deployment client (for a remote container).
In general, the steps to incorporate Arquillian into a build, regardless of what build tool you are using, can be summarized as:
-
Activate/configure the JUnit or TestNG task/plugin
-
Add the Arquillian container integration to the test classpath (e.g., org.jboss.arquillian.container:arquillian-%VENDOR%-%TYPE%-%VERSION%)
-
Add the container runtime (embedded) or deployment client (remote) to the classpath
-
Execute the test build task/goal
If you are only running the Arquillian tests on a single container, this setup is exceptionally straightforward. The challenge comes when you want to run the tests on multiple containers. It’s really just a matter of putting the correct libraries on the test classpath, though.
For some build systems, isolating multiple classpath definitions is more tricky than others. For instance, in Maven, you only get one test classpath per run (without using really advanced plugin configuration). You can toggle between different test classpath pairings through the use of profiles. Each profile contains the libraries for a single target container (a combination of the libraries itemized in steps 2 and 3 above). You’ll see this strategy used in the Arquillian examples.
Other build tools, such as Gradle, can easily define new test tasks that each have their own, unique classpath. This makes it not only possible to separate out the target containers, but also run the tests against each one in the same build execution. We’ll see an example of that later in this chapter. Gradle can also emulate the Maven profile strategy through the use of build fragment imports. We’ll also show an example of that approach for contrast.
2.10.2. Maven
The community has gravitated towards Maven as the preferred build tool for projects that use Arquillian. Details on how to setup Arquillian in a Maven project is covered in detail in the Arquillian Guides.
2.10.3. Gradle
Gradle is a build tool that allows you to create declarative, maintainable, concise and highly-performing builds. More importantly, in this context, Gradle gives you all the freedom you need instead of imposing a rigid build lifecycle on you. You’ll get a glimpse of just how flexible Gradle can be by learning how to integrate Arquillian into a Gradle build.
We’ll be contrasting two strategies for running Arquillian tests from Gradle:
-
Container-specific test tasks
-
Test "profiles" via build fragment imports
The first strategy is the recommended one since it gives you the benefit of being able to run your tests on multiple containers in the same build. However, the second approach is less esoteric and will be more familiar to Maven users. Of course, Gradle is so flexible that there are likely other solutions for this problem. We invite you to give us feedback if you find a better way (or another way worth documenting).
Let’s get the common build stuff out of the way, then dive into the two strategies listed above.
apply from: common
The simplest Gradle build for a Java project is a sparse one line.
apply plugin: JavaPlugin
Put this line into a file named build.gradle at the root of the project, which is the standard location of the Gradle build file. (Perhaps after seeing this configuration you’ll understand the reference in the section title).
Next we’ll add the Maven Central and JBoss Community repository definitions, so that we can pull down dependent libraries. The latter repository hosts the Arquillian artifacts.
apply plugin: JavaPlugin
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}
If your SCM (e.g., SVN, Git) is already ignoring the target directory, you may want to move the Gradle build output underneath this folder, rather than allowing Gradle to use it’s default build directory, build. Let’s add that configuration to the common build logic as well:
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}
If you are using Gradle alongside Maven, you shouldn’t set the buildDir to target since Gradle organizes compiled classes different than Maven does, possibly leading to conflicts (Though, the behavior of Gradle can also be customized).
We also recommend that you centralize version numbers at the top of your build to make upgrading your dependency easy. This list will grow as you add other containers, but we’ll seed the list for the examples below:
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
libraryVersions = [
junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]
...
We also need to add the unit test library (JUnit or TestNG) and the corresponding Arquillian integration:
dependencies {
testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
}
In this example, we’ll assume the project is compiling against APIs that are provided by the target container runtime, so we need to add a dependency configuration (aka scope) to include libraries on the compile classpath but excluded from the runtime classpath. In the future, Gradle will include support for such a scope. Until then, we’ll define one ourselves in the configurations closure.
configurations {
compileOnly
}
We also need to add the dependencies associated with that configuration to the compile classpaths using the sourceSets closure:
sourceSets {
main {
compileClasspath = configurations.compile + configurations.compileOnly
}
test {
compileClasspath = compileClasspath + configurations.compileOnly
}
}
Here’s the Gradle build all together now:
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
libraryVersions = [
junit: '4.8.1', arquillian: '1.0.0.Alpha3', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}
configurations {
compileOnly
}
sourceSets {
main {
compileClasspath = configurations.compile + configurations.compileOnly
}
test {
compileClasspath = compileClasspath + configurations.compileOnly
}
}
Now that the foundation of a build is in place (or you’ve added these elements to your existing Gradle build), we are ready to configuring the container-specific test tasks. In the first approach, we’ll create a unique dependency configuration and task for each container.
Strategy #1: Container-Specific Test Tasks
Each project in Gradle is made up of one or more tasks. A task represents some atomic piece of work which a build performs. Examples include compiling classes, executing tests, creating a JAR, publishing an artifact to a repository. We are interested in the executing tests task. But it’s not necessarily just a single test task. Gradle allows you to define any number of test tasks, each having its own classpath configuration. We’ll use this to configure test executions for each container.
Let’s assume that we want to run the tests against the following three Arquillian-supported containers:
-
Weld EE Embedded 1.1
-
Remote JBoss AS 6
-
Embedded GlassFish 3
We’ll need three components for each container:
-
Dependency configuration (scope)
-
Runtime dependencies
-
Custom test task
We’ll start with the Weld EE Embedded container. Starting from the Gradle build defined in the previous section, we first define a configuration for the test runtime dependencies.
configurations {
compileOnly
weldEmbeddedTestRuntime { extendsFrom testRuntime }
}
Next we add the dependencies for compiling against the Java EE API and running Arquillian tests in the Weld EE Embedded container:
dependencies {
compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi
testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
// temporarily downgrade the weld-ee-embedded-1.1 container
weldEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
weldEmbeddedTestRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
weldEmbeddedTestRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
weldEmbeddedTestRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
weldEmbeddedTestRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j
}
Finally, we define the test task:
task weldEmbeddedTest(type: Test) {
testClassesDir = sourceSets.test.classesDir
classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.weldEmbeddedTestRuntime
}
This task will execute in the lifecycle setup by the Java plugin in place of the normal test task. You run it as follows:
gradle weldEmbeddedTest
Or, more simply:
gradle wET
Now we just repeat this setup for the other containers.
Since you are creating custom test tasks, you likely want to configure the default test task to either exclude Arquillian tests are to use a default container, perhaps Weld EE Embedded in this case.
Here’s the full build file with the tasks for our three target containers:
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
libraryVersions = [
junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
mavenRepo urls: 'http://repository.jboss.org/nexus/content/repositories/deprecated'
}
configurations {
compileOnly
weldEmbeddedTestRuntime { extendsFrom testRuntime }
jbossasRemoteTestRuntime { extendsFrom testRuntime, compileOnly }
glassfishEmbeddedTestRuntime { extendsFrom testRuntime }
}
dependencies {
compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi
testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
// temporarily downgrade the weld-ee-embedded-1.1 container
weldEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
weldEmbeddedTestRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
weldEmbeddedTestRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
weldEmbeddedTestRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
weldEmbeddedTestRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j
jbossasRemoteTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-jbossas-remote-6', version: libraryVersions.arquillian
jbossasRemoteTestRuntime group: 'org.jboss.jbossas', name: 'jboss-as-server', classifier: 'client', version: libraryVersions.jbossas, transitive: false
jbossasRemoteTestRuntime group: 'org.jboss.jbossas', name: 'jboss-as-profileservice', classifier: 'client', version: libraryVersions.jbossas
glassfishEmbeddedTestRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-glassfish-embedded-3', version: libraryVersions.arquillian
glassfishEmbeddedTestRuntime group: 'org.glassfish.extras', name: 'glassfish-embedded-all', version: libraryVersions.glassfish
}
sourceSets {
main {
compileClasspath = configurations.compile + configurations.compileOnly
}
test {
compileClasspath = compileClasspath + configurations.compileOnly
}
}
task weldEmbeddedTest(type: Test) {
testClassesDir = sourceSets.test.classesDir
classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.weldEmbeddedTestRuntime
}
task jbossasRemoteTest(type: Test) {
testClassesDir = sourceSets.test.classesDir
classpath = sourceSets.test.classes + sourceSets.main.classes + files('src/test/resources-jbossas') + configurations.jbossasRemoteTestRuntime
}
task glassfishEmbeddedTest(type: Test) {
testClassesDir = sourceSets.test.classesDir
classpath = sourceSets.test.classes + sourceSets.main.classes + configurations.glassfishEmbeddedTestRuntime
}
Notice we’ve added an extra resources directory for remote JBoss AS 6 to include the required jndi.properties file. That’s a special configuration for the remote JBoss AS containers, though won’t be required after Arquillian 1.0.0.Alpha4.
It’s now possible to run the Arquillian tests against each of the three containers in sequence using this Gradle command (make sure a JBoss AS is started in the background):
gradle weldEmbeddedTest jbossasRemoteTest glassfishEmbeddedTest
Pretty cool, huh?
Now let’s look at another way to solve this problem.
Strategy #2: Test Profiles
Another way to approach integrating Arquillian into a Gradle build is to emulate the behavior of Maven profiles. In this case, we won’t be adding any extra tasks, rather overriding the Java plugin configuration and provided tasks.
A Maven profile effectively overrides portions of the build configuration and is activated using a command option (or some other profile activation setting).
Once again, let’s assume that we want to run the tests against the following three Arquillian-supported containers:
-
Weld EE Embedded 1.1
-
Remote JBoss AS 6
-
Embedded GlassFish 3
All we need to do is customize the test runtime classpath for each container. First, let’s setup the common compile-time dependencies in the main build file:
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
libraryVersions = [
junit: '4.8.1', arquillian: '1.0.0.Alpha3', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}
configurations {
compileOnly
}
dependencies {
group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
}
sourceSets {
main {
compileClasspath = configurations.compile + configurations.compileOnly
}
test {
compileClasspath = compileClasspath + configurations.compileOnly
}
}
We then need to create a partial Gradle build file for each container that contains the container-specific dependencies and configuration. Let’s start with Weld EE Embedded.
Create a file named weld-ee-embedded-profile.gradle and populate it with the following contents:
dependencies {
// temporarily downgrade the weld-ee-embedded-1.1 container
testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-weld-ee-embedded-1.1', version: '1.0.0.Alpha3'
testRuntime group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: libraryVersions.jbossJavaeeSpec
testRuntime group: 'org.jboss.weld', name: 'weld-core', version: libraryVersions.weld
testRuntime group: 'org.slf4j', name: 'slf4j-log4j12', version: libraryVersions.slf4j
testRuntime group: 'log4j', name: 'log4j', version: libraryVersions.log4j
}
Here’s the partial build file for Remote JBoss AS, named jbossas-remote-profile.gradle:
dependencies {
testRuntime group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi
testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-jbossas-remote-6', version: libraryVersions.arquillian
testRuntime group: 'org.jboss.jbossas', name: 'jboss-as-server', classifier: 'client', version: libraryVersions.jbossas, transitive: false
testRuntime group: 'org.jboss.jbossas', name: 'jboss-as-profileservice', classifier: 'client', version: libraryVersions.jbossas
}
test {
classpath = sourceSets.test.classes + sourceSets.main.classes + files('src/test/resources-jbossas') + configurations.testRuntime
}
And finally the one for Embedded GlassFish, named glassfish-embedded-profile.gradle:
dependencies {
testRuntime group: 'org.jboss.arquillian.container', name: 'arquillian-glassfish-embedded-3', version: libraryVersions.arquillian
testRuntime group: 'org.glassfish.extras', name: 'glassfish-embedded-all', version: libraryVersions.glassfish
}
Now we need to import the appropriate partial Gradle build into the main build. The file will be selected based on the value of the project property named profile.
apply plugin: JavaPlugin
buildDir = 'target/gradle-build'
libraryVersions = [
junit: '4.8.1', arquillian: '1.0.0.Alpha4', jbossJavaeeSpec: '1.0.0.Beta7', weld: '1.0.1-Final',
slf4j: '1.5.8', log4j: '1.2.14', jbossas: '6.0.0.Final', glassfish: '3.0.1-b20', cdi: '1.0-SP1'
]
apply from: profile + '-profile.gradle'
repositories {
mavenCentral()
mavenRepo urls: 'http://repository.jboss.org/nexus/content/groups/public'
}
configurations {
compileOnly
}
dependencies {
compileOnly group: 'javax.enterprise', name: 'cdi-api', version: libraryVersions.cdi
testCompile group: 'junit', name: 'junit', version: libraryVersions.junit
testCompile group: 'org.jboss.arquillian', name: 'arquillian-junit', version: libraryVersions.arquillian
}
sourceSets {
main {
compileClasspath = configurations.compile + configurations.compileOnly
}
test {
compileClasspath = compileClasspath + configurations.compileOnly
}
}
Tests are run in the Weld EE Embedded runtime using this command:
gradle test -Pprofile=weld-ee-embedded
That’s pretty much the same experience you get when you use Maven (and a whole heck of a lot simpler).
While the configuration is much simpler using the profiles strategy, there are two things to keep in mind:
-
It crosses over into more than one build file
-
You cannot run the tests in each container in a single build execution
If you have a better idea of how to integrate an Arquillian test suite into a Gradle build, we’d love to hear it on the Arquillian discussion forums.
3. Advanced Topics
3.1. Debugging
If you are struggling with running your Arquillian test and you need more info about what is going on underneath, then you can specify -Darquillian.debug=true
. Arquillian will print out its lifecycle so you will know exactly where it got stuck.
The output has four types of information - you can distinguish them by their prefix:
-
(E) Arquillian event that was fired
-
(I) Arquillian interceptor that observed the related event context
-
(O) Arquillian observer that observed the related event
-
(X) Arquillian extension that was registered
3.2. Test Observer
In case you need to add an additional logic to the whole test suite, you can use Arquillian SPI and create your own Arquillian extension. But this extension is applied to all test classes.
In case you want to add an additional observer that will be applied only to one specific test class, then you can use an annotation @Observer
at the test class level. This will use the specified class(es) as Arquillian observer(s) and observe any Arquillian event in the context of the related test class that you need (starting with BeforeClass
and ending with AfterClass
):
@RunWith(Arquillian.class)
@Observer(MyObserver.class)
public class MyTestCase {
The Arquillian observer class has to have a non-parametric constructor and the observer method should be defined by @Observes
annotation:
public void observeBeforeClass(@Observes BeforeClass event) {
...
}