JUnit Test Groups

Divides your JUnit tests into groups which can be executed separately or all at once

License

License

MIT
Categories

Categories

JUnit Unit Testing
GroupId

GroupId

com.github.ferstl
ArtifactId

ArtifactId

junit-testgroups
Last Version

Last Version

1.0.1
Release Date

Release Date

Type

Type

jar
Description

Description

JUnit Test Groups
Divides your JUnit tests into groups which can be executed separately or all at once
Project URL

Project URL

https://github.com/ferstl/junit-testgroups
Source Code Management

Source Code Management

https://github.com/ferstl/junit-testgroups

Download junit-testgroups

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.ferstl/junit-testgroups/ -->
<dependency>
    <groupId>com.github.ferstl</groupId>
    <artifactId>junit-testgroups</artifactId>
    <version>1.0.1</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.ferstl/junit-testgroups/
implementation 'com.github.ferstl:junit-testgroups:1.0.1'
// https://jarcasting.com/artifacts/com.github.ferstl/junit-testgroups/
implementation ("com.github.ferstl:junit-testgroups:1.0.1")
'com.github.ferstl:junit-testgroups:jar:1.0.1'
<dependency org="com.github.ferstl" name="junit-testgroups" rev="1.0.1">
  <artifact name="junit-testgroups" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.ferstl', module='junit-testgroups', version='1.0.1')
)
libraryDependencies += "com.github.ferstl" % "junit-testgroups" % "1.0.1"
[com.github.ferstl/junit-testgroups "1.0.1"]

Dependencies

compile (1)

Group / Artifact Type Version
junit : junit Optional jar 4.12

test (2)

Group / Artifact Type Version
org.hamcrest : java-hamcrest jar 2.0.0.0
org.mockito : mockito-core jar 1.10.19

Project Modules

There are no modules declared in this project.

JUnit Test Groups

- Divides your JUnit tests into groups which can be executed separately or all at once

Build Status Maven Central Coverage Status license

Background

In a recent project we wanted to write each test as JUnit test no matter if the tests are real unit tests or longer running integration tests. The reason behind this idea is that everyone should be able to run all tests with Maven or directly within the IDE with as less effort as possible. Moreover, we wanted to prevent our CI build and also local Maven builds from executing the integration tests all the time (other CI jobs are executing them). So to make a long story short, we needed a mechanism to group our JUnit tests and to execute these groups of tests independently from each other.

There are already several tools that allow test grouping:

  • JUnit's Categories runner
  • Spring's SpringJunit4ClassRunner and its @IfProfileValue mechanism
  • Maven's Failsafe plugin
  • ...

However, all of these tools have their disadvantages. We are not only using regular JUnit tests but also parameterized tests and Theories. Since these tests require different test runners, JUnit's Categories runner is not an option. Spring's @IfProfileValue mechanism is also not an option for the same reason and the maven-failsafe-plugin does not integrate well with IDEs.

This project tries to achieve the same goals by using JUnit's class rule mechanism. It works pretty well but there are some pitfalls as well (see below).

How to use

Dependencies:

<dependency>
  <groupId>com.github.ferstl</groupId>
  <artifactId>junit-testgroups</artifactId>
  <version>1.0.1</version>
  <scope>test</scope>
</dependency>

<!-- JUnit will not come transitively with junit-testgroups! -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

Grouping tests

First, annotate your tests with @TestGroup and declare the group to which the test belongs. In order to make the test groups work, you also need to define an instance of TestGroupRule as a class rule for each of your tests.

@TestGroup("integration")
public class MyIntegrationTest {
   @ClassRule
   public static TestGroupRule rule = TestGroupRule.create();
   
   ...
}

A test may also belong to several groups:

@TestGroup("group1", "group2")
public class MyTest {
   @ClassRule
   public static TestGroupRule rule = TestGroupRule.create();
   
   ...
}

Tests which don't declare a test group name are in an implicit default group. All tests belonging to the default group will run if no test group is defined (see "Running the Tests" below).

@TestGroup
public class MyRegularUnitTest {
  @ClassRule
  public static TestGroupRule rule = TestGroupRule.create();
}

Instead of defining the same test groups and the class rules over and over again, a more practical approach is defining abstract test classes for all your supported test groups:

@TestGroup("integration")
public abstract class AbstractIntegrationTest {
  @ClassRule
  public static TestGroupRule rule = TestGroupRule.create();
  
  ...
}

public class MyIntegrationTest extends AbstractIntegrationTest {
  @Test
  public void testStuff() { ... }
}

Running the tests

Once your tests are grouped, you can run them by simply defining your test groups to be executed in a system property called testgroup:

  • Execute a simple test group: -Dtestgroup=integration
  • Execute multiple test groups: -Dtestgroup=group1,group2
  • Execute all test groups: -Dtestgroup=all

When no testgroup system property is defined, all tests without an explicitly declared test group will be executed.

More advanced Stuff

Using TestGroupRule together with other Test Rules

JUnit does not make any guarantees in which order test rules are evaluated. Consider this test:

public class MyMultiRuleTest {
  @ClassRule
  public static TestGroupRule testGroupRule = TestGroupRule.create();
  
  @ClassRule
  public static TemporaryFolder tempDir = new TemporaryFolder();
  ...
}

In this scenario it might happen, that the tempDir rule is evaluated before the testGroupRule and creates a temporary directory even if the test is not supposed to run. To create an order between several TestRules you need to use a RuleChain. TestGroupRule provides a convenience factory method for that:

public class MyMultiRuleTest {
  @ClassRule
  public static TestRule rules = TestGroupRule.chain()
      .around(new TemporaryFolder());
  ...
}

This guarantees the TestGroupRule to run first.

Testing with the Spring Framework

The Spring Framework has its own JUnit test runner called SpringJUnit4ClassRunner. When used together with the TestGroupRule this runner will always do some work before any JUnit test rules are executed. So this work is done even if the test is not supposed to run. Since version 4.2 of the Spring Framework there is a rule based alternative to the SpringJUnit4ClassRunner. This allows it to create a rule RuleChains as described above:

@ContextConfiguration
public class MySpringTest {
  @ClassRule
  public static TestRule classRules = TestGroupRule.chain()
      .around(new SpringClassRule());
  
  @Rule
  public MethodRule springMethodRule = new SpringMethodRule();
  
  ...
}

Package Level Grouping

Test groups can also be defined on package level. However, the class rule still needs to be defined in your test classes.

// package-info.java
@TestGroup("integration")
package my.project.integrationtests

Custom Test Group Key

In case the system property key testgroup does not work for you, you can define another key:

@TestGroup(key = "mykey", value = "integration")
public class MyIntegrationTest {
  @ClassRule
  public static TestGroupRule rule = TestGroupRule.create();
  
  ...
}

Tests can then be executed with the system property -Dmykey=integration.

The good Things

  • Works with all test runners extending ParentRunner, which includes all test runners of the JUnit library (BlockJUnit4ClassRunner, Parameterized, Theories, SpringJunit4ClassRunner, etc.).
  • Works with Maven and in your IDE
  • All tests in the (implicit) default group will run without defining anything
  • It's possible to run all tests no matter in what group they are

The bad Things

  • All tests require a @TestGroup annotation and an instance of TestGroupRule as @ClassRule. All other tests will be executed any time.
  • There is no defined execution order of test rules. So if a test is in a test group and uses other test rules, these test rules might get executed even if the test is not supposed to run. You should use RuleChains (with TestGroupRule as first rule) in case you are using other test rules.
  • In case your test runner is a subclass of ParentRunner, the #getChildren() method of your test runner will always be executed, no matter if the test class is supposed to be executed.

Versions

Version
1.0.1
1.0.0