JBoss.orgCommunity Documentation

Chapter 6. Test execution

6.1. Anatomy of a test
6.2. ShrinkWrap packaging
6.3. Test archive deployment
6.4. Enriching the test class
6.5. Negotiating test execution
6.6. Test run modes
6.6.1. Mode: in-container
6.6.2. Mode: as-client

This chapter walks through the details of test execution, covering both the remote and local container cases.

Note

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:

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:

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 use. 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.

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 you 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. Lets dive a bit deeper into the differences between the run modes and see how they effect your test execution and packaging.

@RunWith(Arquillian.class)

@Run(IN_CONTAINER)
public class MyTestCase

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; Arqullian uses this mode by default.

Here is an overview of the expected output of the packaging process when you provide a @Deployment.



Note

In Alpha4 Arquillian only has one type of protocol implementation for communicating with remote servers, the servlet protocol. So the reason for the big difference in packaging between EE 5 and EE 6 containers is mainly due to the support of Web Fragments in the EE 6 specification. Web Fragments lets Arquillian add it self to the @Deployment without drastically changing it, but it also means it has to be a WebArchive in the deployment.

In the next release you will be able to control the packaging your self and we will also look into other protocol implementations like jmx and remote ejb.