Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current Restore this Version View Page History

Version 1 Next »


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>

Simple 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 snippet below shows the structure of the project,
/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

Service Interface


The snippet below shows the Java code for the service interface. The service interface has one operation that is available for the service consumers to invoke.
public interface HelloWorld {
String sayHello(String name);
}

Service Implementation


The service component is implemented as a Java class and implements the interface shown below,
public class HelloWorldImpl implements HelloWorld {
public String sayHello(String name) {
return "Hello, " + name;
}
}

Service Composite


The service composite uses the Service Component Description Language (SCDL) to describe the service. The composite shown below has one component implemented in Java. The implementation class for the Java component is HelloWorldImpl and the service offered by the component is promoted from the composite, so that the service can be used when the composite itself is used in a higher level composite as a composite component.
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="HelloWorldComposite" targetNamespace="urn:helloWorld">
<service name="helloWorldService" promote="HelloWorldComponent"/>
<component name="HelloWorldComponent">
<implementation.java class="HelloWorldImpl"/>
</component>
</composite>

Service Test


The test for the service is written as a standard JUnit test. However, Fabric3 Maven iTest runtime allows unit tests to be written in line with the SCA programming model, and enable the tests to be run in an embedded SCA container, as an SCA component. This means the service being tested can be injected into the test case using SCA semantics.
import org.osoa.sca.annotations.Reference;
import junit.framework.TestCase;
public class HelloWorldITest extends TestCase {
@Reference protected HelloWorld helloWorld;
public void testSayHello() {
assertEquals("Hello, Fred", helloWorld.sayHello("Fred"));
}
}

Integration Composite


The job of a composite file is to wire together components to create more complex components and services which may then be deployed to a domain. The itest.composite file is no exception to this. It allows you to wire together your production contribution and your test classes such that they may be deployed together at which time the test classes can verify that the behaviour of the production contribution is as expected. For
this wiring and deployment to take place, your JUnit test classes must themselves become components and this facility is provided by the special Fabric3 component implementation type junit.
Components of this special type, that is to say JUnit test classes, are represented within the itest.composite in the following way:
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
xmlns:f3="urn:fabric3.org"
name="HelloWorldTestComposite">
<component name="HelloWorldTest">
<f3:junit class="HelloWorldITest"/>
<reference name="helloWorld" target="HelloWorldComposite"/>
</component>
<component name="HelloWorldComposite">
<implementation.composite name="helloWorldComposite" scdlResource="helloWorld.composite" />
</component>
</composite>
The ITest composite contains two components:

  • The test case implemented using the Fabric3 JUnit component. This component has the reference to the original component that is being tested.
  • The component being tested, which is a composite component.


Instead of autowiring the reference to the service, the reference on the JUnit component explictly targets the user component being tested. Please note that implementation.composite belongs to the SCA namespace whereas the junit component is a Fabric3 specific feature and belongs to the Fabric3 implementation namespace. Also, itest.composite is a default name, which the Maven ITest host automatically picks, however, you may use an alternative name and specify that in the plugin configuration.

Maven Project Descriptor


Next, let's look at the Maven project descriptor (pom.xml) that specifies the Fabric3 ITest plugin, that allows the test to be run in an SCA environment.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>helloworld</groupId>
<artifactId>helloworld</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<name>Hello World Test</name>
<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.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-g</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/*ITest.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.fabric3</groupId>
<artifactId>fabric3-itest-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Project Dependencies


The project has the following dependencies:

  • SCA API: This is used for adding the SCA annotations etc


  • JUnit: This is for the purpose of writing the test case.


Plugins


The project uses the following plugins:

  • Java Compiler Plugin: This is to specify the source and target versions as 1.5


  • Fabric3 ITest Plugin: This plugin provides the embedded SCA container in the Maven build environment and allow the JUnit test case to be run as an SCA component. The snippet above uses the default configuration, we will look at the configuration details later. The plugin is bound to the integration test lifecycle phase and integrates with Surefire.


  • Maven Surefire Plugin: This is to exclude any tests with the *ITest pattern to be excluded from the normal surefire run. Otherwise, you will get a NullPointerException, as with normal Surefire runs the reference wouldn't have been injected in the test case.


Running the Test


You can run the test by typing in the mvn integration-test. This will generate the following output:
[INFO] [fabric3-itest:test {execution: default}]
[INFO] Starting Embedded Fabric3 Runtime ...
[INFO] Deploying test composite from c:\projects\tutorials\hellowworld\target\test-classes\itest.composite
[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 Runtime ...
[INFO] ----------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------
[INFO] Total time: 12 seconds
[INFO] Finished at: Fri Dec 19 22:32:25 GMT 2008
[INFO] Final Memory: 18M/33M
[INFO] ----------------------------------------------------------

Using Extensions


Fabric3 ITest hosts, like standalone and web app hosts, provide all the SCA functionailty out of teh box. All the host environments are architected in a modular manner, that allow new features to be added to as extensions to the base runtime. These extensions are released as separate artifacts and users are free to write their own extensions. Extensions provided by Fabric3 include:

  • Binding Extensions
  • Web Services
  • JMS
  • FTP
  • JAX-RS/REST


  • Implementation Extensions
  • Timer Components

  • JPA
  • JTA Transaction and Datasource pooling

Extensions are enabled in the ITest environment by specifying them as plugin configuration using standard Maven dependency conventions. The snippet below shows how the ITest host can be extended to support a custom extension.
<plugin>
<groupId>org.codehaus.fabric3</groupId>
<artifactId>fabric3-itest-plugin</artifactId>
<configuration>
<extensions>
<!-- JPA Hibernate Extensions -->
<dependency>
<groupId>org.codehaus.fabric3</groupId>
<artifactId>fabric3-my-extension</artifactId>
<version>1.0</version>
</dependency>
</configuration>
</plugin>




Working with Easymock


When you write service based applications, services seldom function in isolation. Services may depend on other services for implementing a cohesive piece of functionality. And, of course, dependency from the implementation of one service to another would be through well-defined service contracts. In the previous chapter we saw these dependencies in the SCA world are expressed using references.
When you unit test a service implementation, you may or may not have the implementation of other services it depend on. For example, the implementation of the dependency may come from the same composite, in which case, when you unit test the composite all the dependencies would have been catered for. In other scenarios, implementations of the dependencies may come from extrenal composites, in which case, the references would have been promoted. In such scenarios, you may want to mock those references and verify the behaviour of the composites in terms of the references being called expected number of times.
Fabric3 provides service mocking using the mock implementation using Easymock, which we will cover in this section.

Adding Mock Functionality to Hello World


In this section we will have a look at how service mocking can be used in the hello world example. Let us say each time the sayHello method is called, the component implementation will log the call using a monitor service, whose service contract is shown below:
public interface HelloWorldMonitor {
void onSayHello(String name);
}
The code for the service implementation class has now changed to incorporate the call to the above service:
import org.osoa.sca.annotations.Reference;
public class HelloWorldImpl implements HelloWorld {
@Reference protected HelloWorldMonitor monitor;
public String sayHello(String name) {
monitor.onSayHello(name);
return "Hello, " + name;
}
}
Let us say the monitor service comes from a different composite, so our service composite promotes the reference as shown below, so that it can be provided in the context in which the composite will be used as a component.

<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="HelloWorldComposite" targetNamespace="urn:helloWorld">
<service name="helloWorldService" promote="HelloWorldComponent"/>
<reference name="monitor" promote="HelloWorldComponent/monitor"/>
<component name="HelloWorldComponent">
<implementation.java class="HelloWorldImpl"/>
</component>
</composite>
Now, when we test the above composite the monitor reference will have to be provided. However, in our integration test, rather than using a real implementation of the monitor service, we use a mock implementation using Fabric3 mock support.
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
xmlns:f3="urn:fabric3.org"
name="HelloWorldTestComposite"
autowire="true">
<component name="HelloWorldTest">
<f3:junit class="HelloWorldITest"/>
<reference name="helloWorld" target="HelloWorldComposite"/>
<reference name="monitor" target="MockComponent/HelloWorldMonitor"/>
</component>
<component name="HelloWorldComposite">
<implementation.composite name="helloWorldComposite" scdlResource="helloWorld.composite" />
<reference name="monitor" target="MockComponent/HelloWorldMonitor"/>
</component>
<component name="MockComponent">
<f3:implementation.mock>
HelloWorldMonitor
</f3:implementation.mock>
</component>
</composite>
The implementation type, implementation.mock like junit belongs to the Fabric3 namespace and is an implementation provided by Fabric3 to support mocking service references in integration tests. The implementation takes a list of token separated fully-qualified names of interfaces that need to be mocked.
Note: When autowire is switched on you dont need to explictly specify all the references and target them. This would make the composite less verbose.
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
xmlns:f3="urn:fabric3.org"
name="HelloWorldTestComposite"
autowire="true">
<component name="HelloWorldTest">
<f3:junit class="HelloWorldITest"/>
</component>
<component name="HelloWorldComposite">
<implementation.composite name="helloWorldComposite" scdlResource="helloWorld.composite" />
</component>
<component name="MockComponent">
<f3:implementation.mock>
HelloWorldMonitor
</f3:implementation.mock>
</component>
</composite>
Now in the test code, we can use the Easymock API to verify the right number of calls are made to the monitor service by the HelloWorldImpl component being tested.
import org.osoa.sca.annotations.Reference;
import junit.framework.TestCase;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
public class HelloWorldITest extends TestCase {
@Reference protected HelloWorld helloWorld;
@Reference protected IMocksControl control;
@Reference protected HelloWorldMonitor monitor;
public void testSayHello() {
control.reset();
monitor.onSayHello("Fred");
control.replay();
assertEquals("Hello, Fred", helloWorld.sayHello("Fred"));
control.verify();
}
}
Before you can run the test, you need to modify the POM to add dependency on Easymock and also enable the Fabric3 Easymock extension,
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>helloworld</groupId>
<artifactId>helloworld</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<name>Hello World Test</name>
<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>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
<build>
<defaultGoal>verify</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-g</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/*ITest.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.fabric3</groupId>
<artifactId>fabric3-itest-plugin</artifactId>
<configuration>
<extensions>
<dependency>
<groupId>org.codehaus.fabric3</groupId>
<artifactId>fabric3-mock</artifactId>
<version>RELEASE</version>
</dependency>
</extensions>
<shared>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>2.2</version>
</dependency>
</shared>
</configuration>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>


Configuring Security Performing Security Tests


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.

0 Comments

You are not logged in. Any changes you make will be marked as anonymous. You may want to Log In if you already have an account.