In JUnit 5, how to run code before all tests

JavaJunitJunit5

Java Problem Overview


The @BeforeAll annotation marks a method to run before all tests in a class.

http://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

But is there a way to run some code before all tests, in all classes?

I want to ensure that tests use a certain set of database connections, and the global one-time setup of these connections must occur before running any tests.

Java Solutions


Solution 1 - Java

This is now possible in JUnit5 by creating a custom Extension, from which you can register a shutdown hook on the root test-context.

Your extension would look like this;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
    
public class YourExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {

    private static boolean started = false;

    @Override
    public void beforeAll(ExtensionContext context) {
        if (!started) {
            started = true;
            // Your "before all tests" startup logic goes here
            // The following line registers a callback hook when the root test context is shut down
            context.getRoot().getStore(GLOBAL).put("any unique name", this);
        }
    }

    @Override
    public void close() {
        // Your "after all tests" logic goes here
    }
}

Then, any tests classes where you need this executed at least once, can be annotated with:

@ExtendWith({YourExtension.class})

When you use this extension on multiple classes, the startup and shutdown logic will only be invoked once.

Solution 2 - Java

The already provided answer from @Philipp Gayret has some problems when testing JUnit in parallel (i.e. junit.jupiter.execution.parallel.enabled = true).

Therefore I adapted the solution to:

import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public class BeforeAllTestsExtension extends BasicTestClass
		implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {

	/** Gate keeper to prevent multiple Threads within the same routine */
	private static final Lock LOCK = new ReentrantLock();
    /** volatile boolean to tell other threads, when unblocked, whether they should try attempt start-up.  Alternatively, could use AtomicBoolean. */
    private static volatile boolean started = false;
	
    @Override
	public void beforeAll(final ExtensionContext context) throws Exception {
		// lock the access so only one Thread has access to it
        LOCK.lock();
        try {
    		if (!started) {
    			started = true;
    			// Your "before all tests" startup logic goes here
	    		// The following line registers a callback hook when the root test context is
		    	// shut down
			    context.getRoot().getStore(GLOBAL).put("any unique name", this);

		    	// do your work - which might take some time - 
                // or just uses more time than the simple check of a boolean
		    }
        finally {
            // free the access
    		LOCK.unlock();
        }
	}

	@Override
	public void close() {
		// Your "after all tests" logic goes here
	}
}

As mentioned below JUnit5 provides an automatic Extension Registration. To do so add a in src/test/resources/ a directory called /META-INF/services and add a file named org.junit.jupiter.api.extension.Extension. Add into this file the fully classified name of your class, e.g.

at.myPackage.BeforeAllTestsExtension

Next enable in the same Junit config file

junit.jupiter.extensions.autodetection.enabled=true

With this the extension is attached automatically to all of your tests.

Solution 3 - Java

Extending on suggestion from @Philipp, here's a more complete code snippet:

import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;    
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

public abstract class BaseSetupExtension
    implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {

  @Override
  public void beforeAll(ExtensionContext context) throws Exception {
    // We need to use a unique key here, across all usages of this particular extension.
    String uniqueKey = this.getClass().getName();
    Object value = context.getRoot().getStore(GLOBAL).get(uniqueKey);
    if (value == null) {
      // First test container invocation.
      context.getRoot().getStore(GLOBAL).put(uniqueKey, this);
      setup();
    }
  }

  // Callback that is invoked <em>exactly once</em> 
  // before the start of <em>all</em> test containers.
  abstract void setup();

  // Callback that is invoked <em>exactly once</em> 
  // after the end of <em>all</em> test containers.
  // Inherited from {@code CloseableResource}
  public abstract void close() throws Throwable;
}

How to use:

public class DemoSetupExtension extends BaseSetupExtension {
  @Override
  void setup() {}

  @Override
  public void close() throws Throwable {}
}  

@ExtendWith(DemoSetupExtension.class)
public class TestOne {
   @BeforeAll
   public void beforeAllTestOne { ... }

   @Test
   public void testOne { ... }
}

@ExtendWith(DemoSetupExtension.class)
public class TestTwo {
   @BeforeAll
   public void beforeAllTestTwo { ... }

   @Test
   public void testTwo { ... }
}

Test execution order will be:

  DemoSetupExtension.setup (*)
  TestOne.beforeAllTestOne
  TestOne.testOne
  TestOne.afterAllTestOne
  TestTwo.beforeAllTestTwo
  TestTwo.testTwo
  TestTwo.afterAllTestTwo
  DemoSetupExtension.close (*)

...this will be true regardless if you choose to run a single @Test (e.g. TestOne.testOne), or an entire test class (TestOne), or multiple / all tests.

Solution 4 - Java

You can mark each of your test classes that uses your database with an interface that defines a static BeforeAll (so that it cannot be overridden). e.g.:

interface UsesDatabase {
    @BeforeAll
    static void initializeDatabaseConnections() {
        // initialize database connections
    }
}

This method will be invoked once for each implementing class so you will need to define a way to initialize your connections only once and then do nothing for the other calls.

Solution 5 - Java

I am not aware of a mean to do that.

I would simply make sure that all code for @BeforeAll calls a certain singleton to make that init work (probably in a lazy way to avoid repetition).

Probably not convenient ... the only other option I see: I assume your tests run within a specific JVM job. You could hook an agent into that JVM run, that does that init work for you.

Beyond that: both suggestions sounds somehow like a hack to me. The real answer in my eyes: step back, and carefully examine your environment on its dependencies. And then find a way to prepare your environment in a way that your tests come up and the "right thing" happens automatically. In other words: consider looking into the architecture that bought you this problem.

Solution 6 - Java

Above advises do not work for me, so 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 project run.

Solution 7 - Java

Here's my POC refinement to the very good answer from @Phillip Gayret, following in @Mihnea Giurgea's footsteps.

My sub-question: How to access that shared singleton resource? (Perhaps you are also wondering this...)

Sidebar: During my experimentation, I discovered that using multiple @ExtendWith(...) seems to nest properly. Even so, I remember it not working that way at some point in my fumbling, so you should make sure your usage case(s) are workings rightly [sic].

Because you are probably in a rush, the dessert comes first: Here is the output of running "all tests within the folder":

NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Colors::blue - resource=Something nice to share!
Colors::gold - resource=Something nice to share!
Base::afterAll
Base::beforeAll
Numbers::one - resource=Something nice to share!
Numbers::tre - resource=Something nice to share!
Numbers::two - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)

Of course, just running a single test class gives:

NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Numbers::one - resource=Something nice to share!
Numbers::tre - resource=Something nice to share!
Numbers::two - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)

And a specific test, as can now be expected:

NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Colors::gold - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)

Still with me? Then you might enjoy seeing the actual code...

======================================================
junitsingletonresource/Base.java
======================================================
package junitsingletonresource;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith({Singleton.class})
public abstract class Base extends BaseNestedSingleton
{
    @BeforeAll public static void beforeAll() { System.out.println("Base::beforeAll"); }
    @AfterAll  public static void afterAll () { System.out.println("Base::afterAll" ); }
}

======================================================
junitsingletonresource/Colors.java
======================================================
package junitsingletonresource;

import org.junit.jupiter.api.Test;

public class Colors extends Base
{
    @Test public void blue() { System.out.println("Colors::blue - resource=" + getResource()); }
    @Test public void gold() { System.out.println("Colors::gold - resource=" + getResource()); }
}

======================================================
junitsingletonresource/Numbers.java
======================================================
package junitsingletonresource;

import org.junit.jupiter.api.Test;

public class Numbers extends Base
{
   @Test public void one() { System.out.println("Numbers::one - resource=" + getResource()); }
   @Test public void two() { System.out.println("Numbers::two - resource=" + getResource()); }
   @Test public void tre() { System.out.println("Numbers::tre - resource=" + getResource()); }
}

======================================================
junitsingletonresource/BaseNestedSingleton.java
======================================================
package junitsingletonresource;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;

import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;

/**
 * My riff on Phillip Gayret's solution from: https://stackoverflow.com/a/51556718/5957643
 */
@ExtendWith({BaseNestedSingleton.NestedSingleton.class})
public abstract class BaseNestedSingleton
{
    protected String getResource() { return NestedSingleton.getResource(); }

    static /*pkg*/ class NestedSingleton implements BeforeAllCallback, ExtensionContext.Store.CloseableResource
    {
        private static boolean initialized = false;
        private static String  resource    = "Tests should never see this value (e.g. could be null)";

        private static String getResource() { return resource; }

        @Override
        public void beforeAll(ExtensionContext context)
        {
            if (!initialized) {
                initialized = true;

                // The following line registers a callback hook when the root test context is shut down

                context.getRoot().getStore(GLOBAL).put(this.getClass().getCanonicalName(), this);

                // Your "before all tests" startup logic goes here, e.g. making connections,
                // loading in-memory DB, waiting for external resources to "warm up", etc.

                System.out.println("NestedSingleton::beforeAll (setting resource)");
                resource    = "Something nice to share!";
           }
        }

        @Override
        public void close() {
            if (!initialized) { throw new RuntimeException("Oops - this should never happen"); }

            // Cleanup the resource if needed, e.g. flush files, gracefully end connections, bury any corpses, etc.

            System.out.println("NestedSingleton::close (clearing resource)");
            resource = null;
        }
    }
}

======================================================
junitsingletonresource/Singleton.java
======================================================
package junitsingletonresource;

import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;

import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;

/**
 * This is pretty much what Phillip Gayret provided, but with some printing for traceability
 */
public class Singleton implements BeforeAllCallback, ExtensionContext.Store.CloseableResource
{
    private static boolean started = false;

    @Override
    public void beforeAll(ExtensionContext context)
    {
        if (!started) {
            started = true;
            System.out.println("Singleton::Start-Once");
            context.getRoot().getStore(GLOBAL).put("any unique name", this);
        }
    }

    @Override
    public void close() { System.out.println("Singleton::Finish-Once"); }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRob NView Question on Stackoverflow
Solution 1 - JavaPhilipp GayretView Answer on Stackoverflow
Solution 2 - JavaLeOView Answer on Stackoverflow
Solution 3 - JavaMihnea GiurgeaView Answer on Stackoverflow
Solution 4 - Javamfulton26View Answer on Stackoverflow
Solution 5 - JavaGhostCatView Answer on Stackoverflow
Solution 6 - JavaSergiiView Answer on Stackoverflow
Solution 7 - JavaStevelView Answer on Stackoverflow