
Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:
Once the early-adopter seats are all used, the price will go up and stay at $33/year.
Last updated: January 8, 2024
In this quick article, we’re going to focus on how to run JUnit tests using custom test runners.
Simply put, in order to specify the custom runner, we’ll need to use the @RunWith annotation.
Let’s start by adding the standard JUnit dependency into our pom.xml:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
In the following example, we’ll show how to write our own custom Runner – and run it using @RunWith.
A JUnit Runner is a class that extends JUnit’s abstract Runner class and it is responsible for running JUnit tests, typically using reflection.
Here, we’re implementing abstract methods of Runner class:
public class TestRunner extends Runner {
private Class testClass;
public TestRunner(Class testClass) {
super();
this.testClass = testClass;
}
@Override
public Description getDescription() {
return Description
.createTestDescription(testClass, "My runner description");
}
@Override
public void run(RunNotifier notifier) {
System.out.println("running the tests from MyRunner: " + testClass);
try {
Object testObject = testClass.newInstance();
for (Method method : testClass.getMethods()) {
if (method.isAnnotationPresent(Test.class)) {
notifier.fireTestStarted(Description
.createTestDescription(testClass, method.getName()));
method.invoke(testObject);
notifier.fireTestFinished(Description
.createTestDescription(testClass, method.getName()));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
The getDescription method is inherited from Describable and returns a Description that contains the information that is later being exported and may be used by various tools.
In the run implementation, we’re invoking the target test methods using reflection.
We’ve defined a constructor that takes a Class argument; this is a JUnit’s requirement. At runtime, JUnit will pass the target test class to this constructor.
RunNotifier is used for firing events that have information about the test progress.
Let’s use the runner in our test class:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
@RunWith(TestRunner.class)
public class CalculatorTest {
Calculator calculator = new Calculator();
@Test
public void testAddition() {
Syste.out.println("in testAddition");
assertEquals("addition", 8, calculator.add(5, 3));
}
}
The result we get:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.baeldung.junit.CalculatorTest
running the tests from MyRunner: class com.baeldung.junit.CalculatorTest
in testAddition
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Instead of extending the low-level Runner class, as we did in the last example, we can extend one of the specialized subclasses of Runner: ParentRunner or BlockJUnit4Runner.
The abstract ParentRunner class runs the tests in a hierarchical manner.
BlockJUnit4Runner is a concrete class and if we prefer to customize certain methods, we’ll probably be extending this class.
Let’s see that with an example:
public class BlockingTestRunner extends BlockJUnit4ClassRunner {
public BlockingTestRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
System.out.println("invoking: " + method.getName());
return super.methodInvoker(method, test);
}
}
Annotating a class with @RunWith(JUnit4.class) will always invoke the default JUnit 4 runner in the current version of JUnit; this class aliases the current default JUnit 4 class runner:
@RunWith(JUnit4.class)
public class CalculatorTest {
Calculator calculator = new Calculator();
@Test
public void testAddition() {
assertEquals("addition", 8, calculator.add(5, 3));
}
}
JUnit Runners are highly adaptable and let the developer change the test execution procedure and the whole test process.
If we only want to make minor changes it is a good idea to have a look at the protected methods of BlockJUnit4Class runner.
Some popular third-party implementations of runners for use include SpringJUnit4ClassRunner, MockitoJUnitRunner, HierarchicalContextRunner, Cucumber Runner and much more.