Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

The Maven plug-in is provided by the 'fabric3-itest' mojo which has a single goal 'test'. This goal is bound to the 'integration-test' phase of the Maven life cycle which means that integration tests are run after application packaging and ordinary unit tests. A basic configuration looks like this:

...


<build>
    <defaultGoal>verify</defaultGoal>
    <plugins>
        <plugin>
            <groupId>org.codehaus.fabric3</groupId>
            <artifactId>fabric3-itest-plugin</artifactId>
            <configuration>
                <runtimeVersion>RELEASE</runtimeVersion>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>test</goal>
                    </goals>
                </execution>
            </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 {
    String sayHello(String name);
}

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 {

    @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:

...


<composite name="TestComposite" targetNamespace="urn:fabric3.org" ...>
           
   <component name="HelloWorldTest">
        <f3:junit class="...HelloWorldITest"{color}/>
        <reference name="helloWorld" target="HelloWorldComponent"/>
    </component>
    
    <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:

...


<project ...>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.fabric3.spec</groupId>
            <artifactId>sca-api-r1.0</artifactId>
            <version>0.2.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>verify</defaultGoal>
        <plugins>
             <plugin>
                <groupId>org.codehaus.fabric3</groupId>
                <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

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:

...

  
<plugin>
   <plugin>
      <groupId>org.codehaus.fabric3</groupId>
      <artifactId>fabric3-itest-plugin</artifactId>
      <version>${fabric3.version}</version>
      <configuration>
         <runtimeVersion>${fabric3.version}</runtimeVersion>
         <profiles>
             <profile>
                <groupId>org.codehaus.fabric3</groupId>
                <artifactId>profile-jms</artifactId>
                <version>${fabric3.version}</version>
              </profile>
         </profiles>
         <extensions>
             <extension>
                <groupId>org.codehaus.fabric3</groupId>
                <artifactId>some-extension</artifactId>
                <version>${fabric3.version}</version>
              </extension>
         </extensions>
      </configuration>
      <executions>
         <execution>
            <goals>
               <goal>test</goal>
            </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:

...

  
<plugin>
   <plugin>
      <groupId>org.codehaus.fabric3</groupId>
      <artifactId>fabric3-itest-plugin</artifactId>
      <version>${fabric3.version}</version>
      <configuration>
         <runtimeVersion>${fabric3.version}</runtimeVersion>
         <systemConfig>
            <![CDATA[
               <config>
                  <thread.pool size="100"/>
               </config>
             ]]>
         </systemConfig>
      </configuration>
    .....

   </plugin>
</plugins>

...

JUnit components may be configured with a context for invocations. For example, possible to configure authentication information, which will allow a subject for each test invocation to be set automatically based on that information:
<component name="SecureServiceTest">
<f3:junit class="org.fabric3.policy.security.SecureServiceTest">
<configuration>
<username>foo</username>
<password>bar</password>
</configuration>
</f3:junit>
<reference name="secureRoleService" target="SecureRolesService"/>
</component>
<component name="SecureRolesService">
<implementation.java class="org.fabric3.policy.security.SecureRolesServiceImpl"/>
</component>
The Maven POM is configured with the following:
<systemConfig>
<![CDATA[
<config>
<users>
<user>
<username>foo</username>
<password>bar</password>
<roles>
<role>role1</role>
<role>role2</role>
</roles>
</user>
</users>
</config>
]]>
</systemConfig>

This provides an easy way to test components that require authentication and/or authorization.