JBoss.orgCommunity Documentation
This chapter walks through the details of test execution, covering both the remote and local container cases.
Whilst it's not necessary to understand the details of how Arquillian works, it is often useful to have some insight. This chapter gives you an overview of how Arquillian executes your test for you in your chosen container.
In both JUnit 4 and TestNG 5, a test case is a class which contains at least one test method. The test method
is designated using the @Test
annotation from the respective framework. An Arquillian test
case looks just like a regular JUnit or TestNG test case with two declarative enhancements:
The class contains a static method annotated with @Deployment
that returns a JavaArchive
The class is annotated with @RunWith(Arquillian.class)
(JUnit) or extends
Arquillian
(TestNG)
With those two modifications in place, the test is recognized by the Arquillian test runner and will be executed in the target container. It can also use the extra functionality that Arquillian provides—namely container resource injections and the injection of beans.
When the Arquillian test runner processes a test class, the first thing it does is retrieve the definition of
the Java archive from the @Deployment
method, appends the test class to the archive and
packages the archive using ShrinkWrap.
The name of the archive is irrelevant, so the base name "test" is typically choosen (e.g., test.jar, test.war). Once you have created the shell of the archive, the sky is really the limit of how you can assemble it. You are customizing the layout and contents of the archive to suit the needs of the test. Essentially, you creating a micro application in which to execute the code under test.
You can add the following artifacts to the test archive:
Java classes
A Java package (which adds all the Java classes in the package)
Classpath resources
File system resources
A programmatically-defined file
Java libraries (JAR files)
Other Java archives defined by ShrinkWrap
Consult the ShrinkWrap API to discover all the options you have available for constructing the test archive.
After the Arquillian test runner packages the test archive, it deploys it to the container. For a remote container, this means copying the archive the hot deployment directory or deploying the archive using the container's remote deployment service. In the case of a local container, such as Weld SE, deploying the archive simply means registering the contents of the archive with the runtime environment.
How does Arquillian support multiple containers? And how are both remote and local cases supported? The answer to this question gets into the extensibility of Arquillian.
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.DeployableContainer
. If you recall from the getting started
section, we included an Arquillian library according to the target container we wanted to us. That library
contains an implementation of this interface, thus controlling how Arquillian handles deployment. If you wanted to
introduce support for another container in Arquillian, you would simply provide an implementation of this
interface.
With the archive deployed, all is left is negotiating execution of the test and capturing the results. As you would expect, once all the methods in the test class have be run, the archive is undeployed.
The last operation that Arquillian performs before executing the individual test methods is "enriching" the
test class instance. This means hooking the test class to the container environment by satisfying its injection
points. The enrichment is provided by any implementation of the
org.jboss.arquillian.spi.TestEnricher
SPI on the classpath. Chapter 5, Test enrichment
details the injection points that Arquillian supports.
The question at this point is, how does Arquillian negotiate with the container to execute the test
when the test framework is being invoked locally? Technially the mechanism is pluggable using another SPI,
org.jboss.arquillian.spi.ContainerMethodExecutor
. Arquillian provides a default
implementation for remote servers which uses HTTP communication and an implementation for local tests, which
works through direct execution of the test in the same JVM. Let's have a look at how the remote execution
works.
The archive generator bundles and registers (in the web.xml descriptor) an HttpServlet,
org.jboss.arquillian.protocol.servlet.ServletTestRunner, that responds to test execution GET requests. The test
runner on the client side delegates to the org.jboss.arquillian.spi.ContainerMethodExecutor
SPI implementation, which originates these test execution requests to transfer control to the container JVM.
The name of the test class and the method to be executed are specified in the request query parameters named
className and methodName, respectively.
When the test execution request is received, the servlet delegates to an implementation of the
org.jboss.arquillian.spi.TestRunner
SPI, passing it the name of the test class and the test
method. TestRunner
generates a test suite dynamically from the test class and method name
and runs the suite (now within the context of the container).
The ServletTestRunner
translates the native test result object of JUnit or TestNG into a
org.jboss.arquillian.spi.TestResult
and passes it back to the test executor on the client
side by serializing the translated object into the response. The object gets encoded as either html or a
serialized object, depending on the value of the outputMode request parameter that was passed to the servlet.
Once the result has been transfered to the client-side test runner, the testing framework (JUnit or TestNG)
wraps up the run of the test as though it had been executed in the same JVM.
Now you should have an understanding for how tests can be executed inside the container, but still be executed using existing IDE, Ant and Maven test plugins without any modification. Perhaps you have even started thinking about ways in which you can enhance or extend Arquillian. But there's still one challenge that remains for developing tests with Arquillian. How do you debug test? We'll look at how to hook a debugger into the test execution process in the next chapter.