0

I was trying to find a way to properly implement set up and tear down methods of a test class (for use with JUnit 4).

After trying lots of things and after some reseach I came across this.

Difference between setUp() and setUpBeforeClass()

So... if that's true... I find this design very inflexible.

I mean... In my case I want to run a @BeforeClass method once for the whole test class i.e. once before all test case methods, but after the initialization of my test class instance. And I need this method to be an instance method.

Seems I cannot do it. It is asking me to define the @BeforeClass method as static.

And if I try to use a @Before instance method, I can see that it is called many times... before each test case method. But I need it called just once.

Is there any decent way to do what I want? I am amazed...

Here is the interesting part: for @Before OK... I can do some workaround, I can define some boolean flag and just detect that the current call is the very first @Before method call. And only then do something useful/actual it in.

But for @After I want to detect the very last call of the @After method (so that I actually do something in it only if that's the very last call). But I cannot detect this last call with any flag.

I wonder how the designers of this JUnit thing didn't think of all that.
Or... am I missing something?

peter.petrov
  • 38,363
  • 16
  • 94
  • 159

2 Answers2

1

You can do all of that with JUnit5 (Jupiter JUnit that is). If you really need to use JUnit4 you may want to do something like this:

package com.jesperancinha.unittests;

import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class OneTimeSetupUnitTest extends TestCase {

    private static int up = 0;
    private static int down = 0;

    public static Test suite() {
        return new TestSetup(new TestSuite(OneTimeSetupUnitTest.class)) {
            protected void setUp() {
                System.out.println("testStart");
                up++;
                System.out.println(up);

            }

            protected void tearDown() {
                System.out.println("testEnd");
                down++;
                System.out.println(down);
            }
        };
    }

    @org.junit.Test
    public void test1() {
        System.out.println("test1");
        System.out.println(up);
        System.out.println(down);

    }

    @org.junit.Test
    public void test2() {
        System.out.println("test2");
        System.out.println(up);
        System.out.println(down);
    }
}

The good thing about this is that you'd be doing nothing unexpected including for you scenario.

If you run this code snipet you should get something like:

  • testStart
  • 1
  • test1
  • 1
  • 0
  • test2
  • 1
  • 0
  • testEnd
  • 1

I've placed flags so that I can show you some tracing to exactly explain what's going on. As you can see flag 'up' gets immediately updated upon the start of the unit tests. It doesn't change for the individual tests. Flag 'down' stays with value 0 until the end of the tests. Only at the end this flag is updated. This means that setUp has been called only once and tearDown also only once. You don't need to have both methods, so I think this is what you are looking for.

0

JUnit 4 requires @BeforeClass and @AfterClass to be present only on static methods. If you can't make your method static the easiest way to imitate it is to use a static holder with @After:

private static MyTest lastTest;

public cleanup() {
  // actual instance cleanup
}

@After
public tearDown() {
  lastTest = this;
}

@AfterClass
public static cleanup() {
  if (lastTest != null) {
    lastTest.cleanup();
  }
}

It's an ugly hack that might not work in certain situations and might break the interaction with custom runners e.g. SpringRunner. It might work in simple scenarios.

To answer why neither JUnit 4 nor JUnit 5 doesn't support non-static methods for this use-case see this comment:

Without "test instance per test class" semantics it is required that @BeforeAll and @AfterAll methods be static. See the description of #48 for details.

However, once #48 is resolved, the static restriction will possibly be removed for scenarios in which that makes sense.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • This came to my mind already... But what about After... How do I detect the very last After call? – peter.petrov Dec 07 '18 at 17:42
  • Aha... right, thanks for the idea. That's the solution I was looking for. No, I cannot use a static method with BeforeClass and AfterClass. – peter.petrov Dec 07 '18 at 17:44
  • @peter.petrov counters won't work as `@After` is called before next `@Before` is executed. The only way to tell would be to use test name in the `@After` method if tests are ordered but I'd not recommend this idea. – Karol Dowbecki Dec 07 '18 at 17:44
  • It will work, think about it. The idea is great. I just need to know how many `@Befores` are there..... Ah... maybe you're right. Anyway, thanks. – peter.petrov Dec 07 '18 at 17:46
  • 1
    You could implement your own runner as per [this docs](https://github.com/junit-team/junit4/wiki/test-runners) but honestly it's a waste of time to reinvent `@AfterClass`. Maybe if you say why you can't use `@AfterClass` we can find a solution. – Karol Dowbecki Dec 07 '18 at 17:49
  • Problem with `@AfterClass` is that I need to make too many things static. It requires from you to turn to static stuff only. I need to use instance stuff in `@AfterClass`. I cannot. – peter.petrov Dec 07 '18 at 17:52
  • @peter.petrov save last test class instance in `@After` to be access in `@AfterClass`? – Karol Dowbecki Dec 07 '18 at 17:53
  • Yeah... but then you have the opposite problem. How do you get your instance in `@BeforeClass`. Of course I can do my setup in the very first @Before call but then there would be no symmetry and just I get the feeling we're abusing the whole design :) – peter.petrov Dec 07 '18 at 18:02
  • Yes, time to extract the thing into a separate class and use composition to make it accessible instead of hacking it around. – Karol Dowbecki Dec 07 '18 at 18:02
  • Aha... Or... time to use JUnit 5 as some folks already suggested. – peter.petrov Dec 07 '18 at 18:07