Running JUnit tests with and without before / after interception
I have a situation where I need to be able to run a JUnit test suite in two different modes:
- In the first mode are used
@Before
and@After
annotations / techniques; but... - In the second mode, don't use these annotations / methods, but run the same methods
@Test
For example:
public class WidgetTest {
@Before
void start() {
// Do some start up work.
}
@After
void stop() {
// Do some shutdown work.
}
@Test
public void testWidget() {
// Given/When/Then, etc.
}
}
In "mode # 1" I need methods @Before
and @After
( start()
and stop()
accordingly) to execute before / after testWidget()
. But in "mode # 2" I would only like the method to testWidget()
work.
The best I can think of is this:
public class WidgetTest {
private boolean useHooks;
// Ctor, getter and setter for 'useHooks'.
@Before
void start() {
if(useHooks) {
// Do some start up work.
}
}
@After
void stop() {
if(useHooks) {
// Do some shutdown work.
}
}
@Test
public void testWidget() {
// Given/When/Then, etc.
}
}
But then this presents an additional problem: how do I paste useHooks
into my test suite? This is hacky too, and I think, I hope, JUnit supports this use case out of the box.
Is there a way to do this? If so, how?
source to share
I suggest considering this solution:
- Write a custom JUnit runner
- Add a VM parameter to switch between the two required modes
Here is the custom runner code I came up with:
import java.util.Collections;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
public class MyJUnitRunner extends BlockJUnit4ClassRunner {
private final boolean runBeforesAndAfters = System.getProperty("run-befores-and-afters") != null;
public MyJUnitRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> befores = Collections.EMPTY_LIST;
if (runBeforesAndAfters) {
befores = getTestClass().getAnnotatedMethods(Before.class);
}
return befores.isEmpty() ? statement : new RunBefores(statement, befores, target);
}
@Override
protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> afters = Collections.EMPTY_LIST;
if (runBeforesAndAfters) {
afters = getTestClass().getAnnotatedMethods(After.class);
}
return afters.isEmpty() ? statement : new RunAfters(statement, afters, target);
}
}
All you have to do is start the JUnits VM with the added option: -Drun-befores-and-afters
and annotate your test class with @RunWith(MyJUnitRunner.class)
and then you are done.
source to share
What you are looking for is Parameterized Tests , so the code would look like this (not tested)
@RunWith(Parameterized.class)
public class WidgetTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[] {true, false});
}
private boolean useHooks
public WidgetTest(booelan useHooks) {
this.useHooks = useHooks;
}
....
}
source to share