Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running JUnit tests with and without before/after hooks

Tags:

java

junit

I have a situation where I need to be able to run a suite of JUnit tests under 2 different "modes":

  • With the first mode, use @Before and @After annotations/methods are used; but...
  • With the second mode, do not use these annotations/methods, but run the same @Test methods

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 want @Before and @After methods (start() and stop() respectively) to execute before/after testWidget(). But in "Mode #2", I would only want the testWidget() method to fire.

The best I can come up with is:

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 inject useHooks into my test suite? It is also kind of hacky, and I guess I'm hoping that JUnit supports this kind of use case out of the box.

Is there a way to accomplish this? If so, how?

like image 706
IAmYourFaja Avatar asked Nov 27 '25 05:11

IAmYourFaja


2 Answers

Check out @Category annotation

like image 53
robocoder Avatar answered Nov 28 '25 21:11

robocoder


I suggest to consider this solution:

  • Write a custom JUnit runner
  • Add a VM param to switch between the 2 needed modes

Here is the code of the custom runner I've come 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 it start the JUnits VM with this option added: -Drun-befores-and-afters and annotate your test class with @RunWith(MyJUnitRunner.class) and then you are done.

like image 29
shlomi33 Avatar answered Nov 28 '25 21:11

shlomi33



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!