In the context of the Pineapple project, an integration test is an isolated test of a single class or a set of classes. Dependencies to other classes are satisfied using real classes. Dependencies are managed using dependency injection using Spring. Mocking is used some exceptions to mock special dependencies.
To write integration test do:
The Spring-test jar gives access to:
To use the Spring framework test facilities, add the Maven dependency to the project POM:
<dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.test</artifactId> <scope>test</scope> </dependency>
Version information is located in the pineapple-project/pom.xml. The dependency is scoped as test as it should only be available in the test phase.
The JSR-250 annotations gives access to:
Add the Maven dependency to the project POM:
<dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <scope>test</scope> </dependency>
Version information is located in the pineapple-project/pom.xml. The dependency is scoped as test as it should only be avaiable in the test pahse.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.alpha.pineapple</groupId> <artifactId>pineapple-modules</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>pineapple-infrastructure-test-plugin</artifactId> <packaging>jar</packaging> <name>Pineapple infrastructure test plugin</name> <url>http://maven.apache.org</url> <dependencies> <!-- pineapple internal dependencies --> <dependency> <groupId>${pom.groupId}</groupId> <artifactId>pineapple-api</artifactId> </dependency> <dependency> <groupId>${pom.groupId}</groupId> <artifactId>pineapple-commands-api</artifactId> </dependency> <dependency> <groupId>${pom.groupId}</groupId> <artifactId>pineapple-test-utils</artifactId> <scope>test</scope> </dependency> <!-- external dependencies --> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </dependency> <!-- provides access to @resource annotation --> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> </dependency> <!-- provides access to Spring I18N support --> <dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.context</artifactId> </dependency> <!-- provides access to org.hamcrest matching library --> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> </dependency>
The integration test cases are placed in the src/test/java folder.
Integration tests are located in same folders as the unit tests. The tests are separated by the used naming convention, as shown in the next section.
Test classes are named XxxIntegrationTest where Xxx is the name of the class which is integration tested.
Example: The integration test class for the class TestDeployedConfiguration should be named TestDeployedConfigurationIntegrationTest.
Annotate the class with the @RunWith( SpringJUnit4ClassRunner.class ) Spring annotation to configure the class to be run with the Spring JUnit class runner.
Example: The integration class TestDeployedConfigurationIntegrationTest in the pineapple-weblogic-jmx-plugin-integration-test project is configured with:
Annotate the class with the @ContextConfiguration( locations = "/some-app-config-config.xml" ) Spring annotation to configure the Spring JUnit class runner to load the application context from the designated location(s).
Example: The integration class TestDeployedConfigurationIntegrationTest in the pineapple-helloworld-plugin-integration-test project could be configured with:
package com.alpha.pineapple.plugin.helloworld.operation; import ...; /** * Integration test for the <code>TestDeployedConfiguration</code> class. */ @RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( locations = { "/com.alpha.pineapple.plugin.helloworld-config.xml" } ) public class TestDeployedConfigurationIntegrationTest { // class content goes here... }
An instance of the class under test, should be obtained by using the annotation based dependency injection mechanism in Spring. To enable it:
When the tests is run Spring will initialize the field with the bean defined in the application context file.
Example: The integration class TestDeployedConfigurationIntegrationTest in the pineapple-helloworld-plugin project, could be implemented with the field TestDeployedConfiguration operation; which is annotated with the @Resource annotation:
package com.alpha.pineapple.plugin.helloworld.operation; import ...; /** * Integration test for the <code>TestDeployedConfiguration</code> class. */ @RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( locations = { "/com.alpha.pineapple.plugin.helloworld-config.xml" } ) public class TestDeployedConfigurationIntegrationTest { /** * Object under test. */ @Resource TestDeployedConfiguration testDeployedConfiguration; // remaining class content goes here... }
In order to enable Spring to inject the class under test (as described in the previous paragraph) it is required to add the class to Spring context configuration.
Some classes are added to the context in order for them to be injected, e.g. command classes and the message provider. But other classes are discovered by the class scanning as part of the plugin framework initialized of plugins, e.g. operation classes. The bean is defined in the application context file for the pineapple-helloworld-plugin project:
<bean id="testDeployedConfiguration" class="com.alpha.pineapple.plugin.helloworld.operation.TestDeployedConfiguration" />
There are two approaches to define mock objects for plugin providers in integration tests:
If an integration test requires usage of plugin providers, then a mock provider can be defined in the Spring application context using the Spring feature classed environment profile.
An environment profile allows for the definition of beans which are only accessible when the test is executed with the profile defined as being active.
To enable it:
<!-- definitions used by the integration tests. Only available for test with @ActiveProfiles("integration-test") --> <beans profile="integration-test" > <bean id="defaultOperation" class="com.alpha.pineapple.plugin.agent.operation.DefaultOperation" /> <bean id="coreAdministrationProvider" class="org.easymock.EasyMock" factory-method="createMock" primary="true" > <constructor-arg value="com.alpha.pineapple.admin.AdministrationProvider"/> </bean> <bean id="coreExecutionInfoProvider" class="org.easymock.EasyMock" factory-method="createMock"> <constructor-arg value="com.alpha.pineapple.execution.ExecutionInfoProvider"/> </bean> <bean id="coreRuntimeDirectoryProvider" class="org.easymock.EasyMock" factory-method="createMock"> <constructor-arg value="com.alpha.pineapple.io.file.RuntimeDirectoryProvider"/> </bean> </beans>
@ActiveProfiles("integration-test") @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({ DirectoryTestExecutionListener.class, DependencyInjectionTestExecutionListener.class }) @ContextConfiguration(locations = { PLUGIN_APP_CONTEXT }) public class CloneRepositoryCommandSystemTest {
The plugin framework defines two plugin providers which can provide a plugin with useful information at runtime:
The providers are injected into the plugin application context as beans when Pineapple is initialized. During integration test of classes in an plugin project which uses these providers, the provider beans must be present in the plugin application context or the test will fail due to dependency injection errors.
The solution is to use the test context configuration named com.alpha.pineapple.testutils-config.xml which is defined in the the pineapple-test-utils project.
The test context configuration contains bean definitions of the plugin provider whose implementations are uninitialized EasyMock objects:
<!-- definition of MOCK runtime directory provider --> <bean id="runtimeDirectoryProvider" class="org.easymock.EasyMock" factory-method="createMock"> <constructor-arg value="com.alpha.pineapple.io.file.RuntimeDirectoryProvider"/> </bean> <!-- definition of MOCK execution info provider --> <bean id="executionInfoProvider" class="org.easymock.EasyMock" factory-method="createMock"> <constructor-arg value="com.alpha.pineapple.execution.ExecutionInfoProvider"/> </bean>
To use test context configuration follow these steps:
package com.alpha.pineapple.plugin.helloworld.operation; import ...; /** * Integration test for the <code>TestDeployedConfiguration</code> class. */ @RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( locations = { "/com.alpha.pineapple.testutils-config.xml", "/com.alpha.pineapple.plugin.helloworld-config.xml" } ) public class TestDeployedConfigurationIntegrationTest { // class content goes here... }
public class TestDeployedConfigurationIntegrationTest { /** * Mock Runtime directory provider. */ @Resource RuntimeDirectoryProvider coreRuntimeDirectoryProvider; }
@Before public void setUp() throws Exception { // reset plugin provider EasyMock.reset(coreRuntimeDirectoryProvider); // more stuff here }
If the projects doesn't define any log4j.properties>> in <<<src/java/resources/log4.properties then the pineapple-test-utils project contains a log4j configuration file defined in the directory src/java/resources/log4.properties for use by dependent projects as part of their test configuration.
The test configuration configures Log4j to write log files to ${user.home}/.pineapple/logs/pineapple-.log.