I've inherited a load of Junit test, but these tests (apart from most not working) are a mixture of actual unit test and integration tests (requiring external systems, db etc).
So I'm trying to think of a way to actually separate them out, so that I can run the unit test nice and quickly and the integration tests after that.
The options are..
Split them into separate directories.
Move to Junit4 (from v3) and annotate the classes to separate them.
Use a file naming convention to tell what a class is , i.e. AdapterATest and AdapterAIntergrationTest.
3 has the issue that Eclipse has the option to "Run all tests in the selected project/package or folder". So it would make it very hard to just run the integration tests.
2: runs the risk that developers might start writing integration tests in unit test classes and it just gets messy.
1: Seems like the neatest solution, but my gut says there must be a better solution out there.
So that is my question, how do you lot break apart integration tests and proper unit tests?
You can split them very easily using JUnit categories and Maven.
This is shown very, very briefly below by splitting unit and integration tests.
Define A Marker Interface
The first step in grouping a test using categories is to create a marker interface.
This interface will be used to mark all of the tests that you want to be run as integration tests.
public interface IntegrationTest {}
Mark your test classes
Add the category annotation to the top of your test class. It takes the name of your new interface.
import org.junit.experimental.categories.Category;
@Category(IntegrationTest.class)
public class ExampleIntegrationTest{
@Test
public void longRunningServiceTest() throws Exception {
}
}
Configure Maven Unit Tests
The beauty of this solution is that nothing really changes for the unit test side of things.
We simply add some configuration to the maven surefire plugin to make it to ignore any integration tests.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.11</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>2.12</version>
</dependency>
</dependencies>
<configuration>
<includes>
<include>**/*.class</include>
</includes>
<excludedGroups>com.test.annotation.type.IntegrationTest</excludedGroups>
</configuration>
</plugin>
When you do a mvn clean test only your unmarked unit tests will run.
Configure Maven Integration Tests
Again the configuration for this is very simple.
To run only the integration tests, use this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.11</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>2.12</version>
</dependency>
</dependencies>
<configuration>
<groups>com.test.annotation.type.IntegrationTest</groups>
</configuration>
</plugin>
If you wrap this in a profile with id IT
, you can run only the fast tests using mvn clean install
. To run just the integration/slow tests, use mvn clean install -P IT
.
But most often, you will want to run the fast tests by default and all tests with -P IT
. If that's the case, then you have to use a trick:
<profiles>
<profile>
<id>IT</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludedGroups>java.io.Serializable</excludedGroups> <!-- An empty element doesn't overwrite, so I'm using an interface here which no one will ever use -->
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
As you can see, I'm excluding tests that are annotated with java.io.Serializable
. This is necessary because the profile will inherit the default config of the Surefire plugin, so even if you say <excludedGroups/>
or <excludedGroups></excludedGroups>
, the value com.test.annotation.type.IntegrationTest
will be used.
You also can't use none
since it has to be an interface on the classpath (Maven will check this).
Notes:
surefire-junit47
is only necessary when Maven doesn't switch to the JUnit 4 runner automatically. Using the groups
or excludedGroups
element should trigger the switch. See here.@RunWith()
annotations to run suites or Spring-based tests.