139

I set up a class with a couple of tests and rather than using @Before I would like to have a setup method that executes only once before all tests. Is that possible with Junit 4.8?

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Bober02
  • 15,034
  • 31
  • 92
  • 178

14 Answers14

231

Although I agree with @assylias that using @BeforeClass is a classic solution it is not always convenient. The method annotated with @BeforeClass must be static. It is very inconvenient for some tests that need instance of test case. For example Spring based tests that use @Autowired to work with services defined in spring context.

In this case I personally use regular setUp() method annotated with @Before annotation and manage my custom static(!) boolean flag:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}
Community
  • 1
  • 1
AlexR
  • 114,158
  • 16
  • 130
  • 208
  • 13
    Adding to Kenny Cason's comment as to why it must be static. It must be static because JUnit instantiates a new instance of the test class for each @Test method. The instance variable will be reset to it's default value (false) for each instance if it's not static. See for more info: http://martinfowler.com/bliki/JunitNewInstance.html – dustin.schultz Apr 23 '14 at 21:53
  • 2
    This works except in the case where the `setUp()` method is in a superclass - have posted an [answer](http://stackoverflow.com/questions/12087959#31117171) below attempting to resolve this. – Steve Chambers Jun 29 '15 at 13:38
  • 4
    I hesitate to say this to someone with an 84k rep, but BeforeClass doesn't in fact answer the question: BeforeClass is run at the beginning of every test class. But the OP asked for one which runs "only once before all tests". Your proposed solution could do this, but you'd have to make all your test classes extend a "CommonTest" class... – mike rodent Jan 15 '17 at 19:12
  • 1
    @mikerodent, IMHO OP asked about all tests within his test case, not all tests overall. So, your comment is less relevant. BTW, do not worry to say anything to any person even if his reputation is high. At least this is what I do :). And my reputation was significantly lower at Aug 2012 when I answered the question. – AlexR Jan 16 '17 at 09:03
  • 1
    Doesn't work for my case, variables initialized in the setup are reseted after each test, so it's pointless to init only once. – Aphax May 24 '17 at 16:58
  • This should NOT be an accepted answer, no answer I can see here can. Note that JUnit execution engines can use multiple runners (separate JVMs) so static "setUpIsDone" flag won't work guarantee singular execution. – Learner Dec 05 '21 at 03:16
96

You can use the BeforeClass annotation:

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 18
    I cannot use this, I have e few setup methods that are based on non-static components such as getClass() – Bober02 Aug 23 '12 at 09:06
  • 1
    @Bober02 BeforeClass needs to be static indeed. If you can't use that, the other answer provides a workaround. – assylias Aug 23 '12 at 09:32
  • 2
    Sure you cannot use `TheClassYouWant.class` instead of your getClass() call? This is actual Java: `String.class.getName()`. – stolsvik Apr 10 '13 at 10:44
  • http://stackoverflow.com/questions/18574513/junit-run-setup-only-once-for-a-large-number-of-test-classes/22186630#22186630 – skipy Mar 05 '14 at 01:21
  • This looks promising.. I had the same problem as @Bober02 but will keep this mind for next time.. – speedynomads Jan 06 '17 at 20:50
  • I hesitate to say this to someone with a 184k rep (!), but BeforeClass doesn't in fact answer the question: BeforeClass is run at the beginning of every test class. But the OP asked for one which runs "only once before all tests". – mike rodent Jan 15 '17 at 19:12
  • 1
    @mikerodent I understood the question as "all tests in the class" - but you're right it may not be what the OP wanted. – assylias Jan 15 '17 at 20:29
  • Yep, sorry, my silliness in not reading the question properly. – mike rodent Jan 16 '17 at 20:49
32

JUnit 5 now has a @BeforeAll annotation:

Denotes that the annotated method should be executed before all @Test methods in the current class or class hierarchy; analogous to JUnit 4’s @BeforeClass. Such methods must be static.

The lifecycle annotations of JUnit 5 seem to have finally gotten it right! You can guess which annotations available without even looking (e.g. @BeforeEach @AfterAll)

Brian
  • 13,412
  • 10
  • 56
  • 82
  • 11
    It has the same problem was `@BeforeClass`, it needs to be `static`. IMO @AlexR's solution is nicer. – zengr Dec 13 '16 at 19:52
  • @zengr tend to agree with you: as I have said to AlexR, his solution requires all the test classes to subclass from a CommonTest class if it is only to run once. But it is simple as simple can be, and IMHO you probably shouldn't use a "fancy" framework-supplied solution when a simple mechanism is available from the language. Unless there's a good reason of course. Also, using a simple thing like his, with a good "does what it says on the tin" type name, helps with readability. – mike rodent Jan 15 '17 at 19:24
  • Having said this, again IMHO, there seems much more justification for having an "AfterAll" annotation: it would be very difficult and contrived to devise a mechanism for detecting when all tests were done. Conversely, of course, purists are probably going to say you should never have to do a "final cleanup", i.e. that each "tearDown" should leave all resources in a pristine state... and they're probably right! – mike rodent Jan 15 '17 at 19:27
  • Does this work with Maven where there are multiple modules, each with their tests? – Mark Boon Mar 13 '17 at 16:12
  • @mike rodent, in my case setting up and tearing down test files in the filesystem before/after each test seems to be leading to deadlocks on the files. For now, I've arrived independently at AlexR's solution to setting up once. I have two static flags, alreadySetup and dirty. setup() calls cleanup() if a dirty state is detected initially, or if a setup failure leads to a dirty state. To clean up after running tests, I run them again. Messy, not ideal at all, not in our build process. Still looking for a better way (jUnit 4.12). – Rebeccah Apr 07 '17 at 18:22
  • I think you still have to use this information to get the desired effect. "If you would prefer that JUnit Jupiter execute all test methods on the same test instance, simply annotate your test class with @TestInstance(Lifecycle.PER_CLASS). When using this mode, a new test instance will be created once per test class. Thus, if your test methods rely on state stored in instance variables, you may need to reset that state in (at)BeforeEach or (at)AfterEach methods." – crowmagnumb Oct 31 '17 at 18:40
10

When setUp() is in a superclass of the test class (e.g. AbstractTestBase below), the accepted answer can be modified as follows:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

This should work for a single non-static setUp() method but I'm unable to produce an equivalent for tearDown() without straying into a world of complex reflection... Bounty points to anyone who can!

Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
7

JUnit 5 @BeforeAll can be non static provided the lifecycle of the test class is per class, i.e., annotate the test class with a @TestInstance(Lifecycle.PER_CLASS) and you are good to go

Deepak
  • 3,648
  • 1
  • 22
  • 17
4

Edit: I just found out while debugging that the class is instantiated before every test too. I guess the @BeforeClass annotation is the best here.

You can set up on the constructor too, the test class is a class after all. I'm not sure if it's a bad practice because almost all other methods are annotated, but it works. You could create a constructor like that:

public UT () {
    // initialize once here
}
@Test
// Some test here...

The ctor will be called before the tests because they are not static.

2

Use Spring's @PostConstruct method to do all initialization work and this method runs before any of the @Test is executed

Abhishek Chatterjee
  • 1,962
  • 2
  • 23
  • 31
0

Try this solution: https://stackoverflow.com/a/46274919/907576 :

with @BeforeAllMethods/@AfterAllMethods annotation you could execute any method in Test class in an instance context, where all injected values are available.

radistao
  • 14,889
  • 11
  • 66
  • 92
0

My dirty solution is:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

I use it as a base base to all my testCases.

codeporn
  • 1,000
  • 1
  • 13
  • 32
Obi Two
  • 1
  • 1
  • 5
  • public class TestCaseExtended extends TestCase { private static boolean isInitialized = false; private static TestCaseExtended caseExtended; private int serId; @Override public void setUp() throws Exception { super.setUp(); if (!isInitialized) { caseExtended = new TestCaseExtended(); caseExtended.loadSaveNewSerId(); caseExtended.emptyTestResultsDirectory(); isInitialized = true; } } – Obi Two Mar 09 '18 at 10:16
0

If you don't want to force a declaration of a variable that is set and checked on each subtest, then adding this to a SuperTest could do:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}
mjs
  • 21,431
  • 31
  • 118
  • 200
0

I solved this problem like this:

Add to your Base abstract class (I mean abstract class where you initialize your driver in setUpDriver() method) this part of code:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

And now, if your test classes will extends from Base abstract class -> setUpDriver() method will be executed before first @Test only ONE time per run.

Sergii
  • 121
  • 1
  • 5
0

After experimenting for some time this is my solution. I needed this for spring boot test. I tried using @PostConstruct, unfortunately it is executed for every test.

public class TestClass {
    private static TestClass testClass = null;
    @Before
    public void setUp() {
        if (testClass == null) {
            // set up once
            ...
            testClass = this;
        }
    }
    @AfterClass
    public static void cleanUpAfterAll() {
        testClass.cleanUpAfterAllTests();
    }
    private void cleanUpAfterAllTests() {
        // final cleanup after all tests
        ...
    }
    @Test
    public void test1() {
        // test 1
        ...
    }
    @Test
    public void test2() {
        // test 2
        ...
    }
}
user123456789
  • 53
  • 1
  • 9
0

Here is one alternative suggestion:

What I do to get this working is Create a method named _warmup or just _ Annotate the test class with @FixMethodOrder(MethodSorters.NAME_ASCENDING)

This is applicable only if you run all tests in the class

It has a downside of having additional test included, which will also run one additional @Before and @After It is usually advised for your test methods to be order independent, this breaks that rule, but why someone would like tests ordered randomly in the reports I have no clue so NAME_ASCENDING is what I always use

But the upsides to this is simple setup with minimal code and without the need to extend classes/runners etc... Test run lengths are more accurate since all setup time is reported on method _warmup

Igor Čordaš
  • 5,785
  • 4
  • 42
  • 54
0

The problem with @BeforeClass is that it fires before the constructor. So if you rely on an @Autowired constructor to provide data, it simply will not work: wrong execution order.

Similarly @PostConstruct fires after the constructor has been called. And the constructor fires with every @Test, therefore your setup function will fire with every test, if you use this. This has the exact same effect as calling a function from the constructor.

The only solution, I found that works, is to use a flag to indicate if the setUp() method has already been executed. While its not ideal, it will drastically reduce the amount of processing before each test.

private static boolean initialized = false;    

@Autowired
public CacheTest( MyBean myBean ){

    this.myBean = myBean;
}


@PostConstruct
public static void setUp(){

    if( initialized ) { return };

    initialized = true;
    
    //do suff with myBean
}
PowerAktar
  • 2,341
  • 1
  • 21
  • 17