Bober02
Bober02

Reputation: 15331

Junit - run set up method once

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?

Upvotes: 143

Views: 179039

Answers (14)

PowerAktar
PowerAktar

Reputation: 2428

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
}

Upvotes: 0

user123456789
user123456789

Reputation: 83

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
        ...
    }
}

Upvotes: 0

Igor Čordaš
Igor Čordaš

Reputation: 5954

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

Upvotes: 0

Deepak
Deepak

Reputation: 4052

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

Upvotes: 8

AlexR
AlexR

Reputation: 115328

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;
}

Upvotes: 234

Steve Chambers
Steve Chambers

Reputation: 39384

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!

Upvotes: 11

Abhishek Chatterjee
Abhishek Chatterjee

Reputation: 2049

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

Upvotes: 2

Sergii
Sergii

Reputation: 171

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.

Upvotes: 0

mjs
mjs

Reputation: 22347

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 ... 
    }

}

Upvotes: 0

Obi Two
Obi Two

Reputation: 1

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.

Upvotes: 0

radistao
radistao

Reputation: 15504

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.

Upvotes: 0

Brian
Brian

Reputation: 13571

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)

Upvotes: 31

user5692355
user5692355

Reputation:

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.

Upvotes: 4

assylias
assylias

Reputation: 328568

You can use the BeforeClass annotation:

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

Upvotes: 96

Related Questions