Automated tests are a very useful way of improving code quality. They provide a context for development, documentation, and an on-going check of code correctness. These benefits give you the confidence to build on and refactor code knowing that it will continue to work as designed. This chapter covers the application testing facilities provided by Fabric3. Some familiarity with Maven, JUnit and the concepts of mock objects are assumed.
Unit Testing
Java-based components in SCA are in most cases simple POJOs with optional annotations. This means that this guide has little to add to the already well-covered topics of test-driven development and unit testing. It is important to note, however, that even with Fabric3's strong support for integration testing (examined below), it remains easier to find and fix a whole class of bugs by running lightweight tests as opposed to a (slower) test-harness.
Integration Testing
Fabric 3 integration tests ensure that component implementations and composites provide the expected functionality and interact with other services and runtime resources in the expected way. They are referred to as 'itests'.
While Fabric 3 does provide a lightweight standalone runtime environment, the recommended way to drive automated integration tests is through the specialized Maven plug-in. This handles the creation of an embedded Fabric3 runtime, deployment, and test execution. Fast runtime bootstrap and test execution makes iterative development easier. Since the Maven plug-in provides the same execution environment as the standalone server, component behavior verification can be done with little overhead. Use of the Maven plug-in also allows integration tests to be initiated in exactly the same way on development machines and the build server.
...
Configuring Profiles and Extensions
The ITest plugin supports the full range of Fabric3 extensions and profiles such as JTA, JMS, Web Services and REST. Profiles and extensions are enabled using the <profiles> and <extensions> elements respectively:
Code Block | ||||
---|---|---|---|---|
| ||||
<build> <plugin> <defaultGoal>verify</defaultGoal> <plugins> <plugin> <groupId>org.codehaus.fabric3</groupId> <artifactId>fabric3-itest-plugin</artifactId> <configuration> <runtimeVersion>RELEASE</runtimeVersion> </configuration><version>${fabric3.version}</version> <configuration> <executions> <execution> <goals> <goal>test</goal> </goals><runtimeVersion>${fabric3.version}</runtimeVersion> <profiles> </execution> <profile> </executions> </plugin> </plugins> </build> |
Integration Test Example
The example illustrates the use of Fabric3 ITest plugin for testing a simple SCA service. The service will be injected into a JUnit test case using the SCA Java programming model, and the test case will invoke the injected service in the ITest host environment. The example uses:
- Service interface
- Service implementation
- Test class
- Service composite
- Test composite
- Maven project descriptor
The figure below shows the structure of the project:
Code Block |
---|
/pom.xml
/src/main/java/HelloWorld.java
/src/main/java/HelloWorldImpl.java
/src/main/resources/helloWorld.composite
/src/test/java/HelloWorldITest.java
/src/test/resources/itest.composite
|
Creating the Service Interface, Implementation and Composite
The service interface and component implementation are as follows:
Code Block |
---|
{public interface HelloWorld { <groupId>org.fabric3</groupId> String sayHello(String name); } <artifactId>profile-jms</artifactId> public class HelloWorldImpl implements HelloWorld { public String sayHello(String name) { return "Hello " + name; } } |
The component is configured in the following composite:
...
<composite name="HelloComposite" targetNamespace="urn:fabric3.org:sample" ...>
<component name="HelloWorldComponent">
<implementation.java class="...HelloWorldImpl"/>
</component>
</composite>
Setting up the Integration Test
The test for the service is written as a standard JUnit test which is configured as an SCA component and placed under /src/test/java. This means the service being tested can be injected into the test case using SCA:
Code Block |
---|
public class HelloWorldITest extends TestCase {<version>${fabric3.version}</version> @Reference protected HelloWorld helloWorld; public void testSayHello() { assertEquals({"Hello, Foo", helloWorld.sayHello("Foo")); } } |
The test case is then configured in a special test composite that includes the application composite. The test composite should be names "TestComposite" with a target namespace of "urn:fabric3.org" and placed under /src/test/resource:
Code Block | ||
---|---|---|
xml | xml | <composite name="TestComposite" targetNamespace="urn:fabric3.org" ...> </profile> <component name="HelloWorldTest"> <f3:junit class="...HelloWorldITest"{color}/> <reference name="helloWorld" target="HelloWorldComponent"/> </component></profiles> <include name="HelloWorldComposite/> </composite> |
The Maven POM
The Maven POM must specify the SCA API and JUnit as dependencies. It must also configure the Fabric3 ITest plugin:
Code Block | ||
---|---|---|
xml | xml | <project ...><extensions> <dependencies> <extension> <dependency> <groupId>org.codehaus.fabric3.spec<fabric3</groupId> <artifactId>sca-api-r1.0</artifactId> <version>0.2.1</version><artifactId>some-extension</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version><version>${fabric3.version}</version> </dependency> </dependencies>extension> <build> <defaultGoal>verify</defaultGoal></extensions> <plugins> <plugin> <groupId>org.codehaus.fabric3</groupId> </configuration> <artifactId>fabric3-itest-plugin</artifactId> <executions> <execution> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> |
Running the Tests
The tests are run executing mvn integration-test. This will boot the Fabric3 runtime, deploy the test composite, and call all test operations on configured JUnit components. Test results will then be collocated and reported. The following output will be generated when the plugin is run:
Code Block |
---|
[INFO] [fabric3-itest:test {execution: default}]
[INFO] Starting Fabric3 Runtime ...
[INFO] JMX management extension installed
[INFO] JMX connector started on port 1199
[INFO] iTestContribution installed
[INFO] Composite {urn:fabric3.org}FunctionTestHarnessComposite deployed
[INFO] Executing tests...
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.032 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] Stopping Fabric3 {color:#831b92}Runtime{color} ...
[INFO] ----------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------
[INFO] Total time: 12 seconds
|
End-to-End Integration Tests
It is also possible to conduct end-to-end integration tests by binding a JUnit component reference to a transport. The previous example could be modified to invoke the HelloWorldService over JMS (the JMS profile will need to be installed as detailed in the next section):
...
<composite name="TestComposite" targetNamespace="urn:fabric3.org" ...>
<component name="HelloWorldTest">
<f3:junit class="...HelloWorldITest"{color}/>
<reference name="helloWorld" target="HelloWorldComponent">
<binding.jms>
<destination jndiName="Queue"/>
</binding.jms>
</reference>
</component>
<include name="HelloWorldComposite/>
</composite>
Using Profiles and Extensions
...
Specifying a System Configuration
The Maven runtime systemConfig is specified inline as part of the plugin <systemConfig> element in a CDATA section. It is the same format as the other Fabric3 runtime systemConfig.xml files:
Code Block | ||||
---|---|---|---|---|
| ||||
<plugin> <plugin> <groupId>org.codehaus.fabric3</groupId> <artifactId>fabric3-itest-plugin</artifactId> <version>${fabric3.version}</version> <configuration> <runtimeVersion>${fabric3.version}</runtimeVersion> <profiles><systemConfig> <![CDATA[ <profile> <config> <groupId>org.codehaus.fabric3</groupId> <artifactId>profile-jms</artifactId> <thread.pool size="100"/> <version>${fabric3.version}</version>config> </profile>]]> </profiles>systemConfig> </configuration> <extensions> ..... </plugin> </plugins> |
Multi-Module Tests
It is possible to configure the iTest plugin to test multiple contributions that are built from separate Maven modules. These modules may be part of the current project or contained in a separate project.
To deploy separate contributions to the iTest plugin, use the contributions element with nested dependency elements:
Code Block | ||||
---|---|---|---|---|
| ||||
<build> <plugins> <extension> <plugin> <groupId>org.codehaus.fabric3</groupId> <artifactId>some-extension</artifactId> <artifactId>fabric3-itest-plugin</artifactId> <version>${fabric3.version}</version> <configuration> </extension> <contributions> </extensions> </configuration> <executions><dependency> <execution> <goals> <goal>test</goal><groupId>org.fabric3.tests</groupId> </goals> </execution> </executions> </plugin> </plugins> |
Configuring the Maven Runtime
The Maven runtime systemConfig is specified inline as part of the plugin <systemConfig> element in a CDATA section. It is the same format as the other Fabric3 runtime systemConfig.xml files:
Code Block | ||
---|---|---|
xml | xml | <plugin> <plugin> <artifactId>test-multiple-application</artifactId> <groupId>org.codehaus.fabric3</groupId> <artifactId>fabric3-itest-plugin</artifactId> <version>${fabric3project.version}</version> <configuration> <runtimeVersion>${fabric3.version}</runtimeVersion> dependency> <systemConfig> <![CDATA[ <config> <thread.pool size="100"/>/contributions> </config>configuration> ]]>.... </systemConfig> plugin> </configuration> ..... </plugin> </plugins> |
...
< |
...
/ |
...
build> |
...