In JUnit 5, how to run code before all tests
Solution 1
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
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
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
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
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.
Comments
-
Rob N almost 3 years
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.
-
kapex over 5 yearsIf you need to execute this for every test class, then you could also use automatic extension regristration to register the extension using a ServiceLoader. Then you don't need to annotate every single test (with the risks that you might miss some).
-
Emmanuel Touzery over 4 yearsin my case the
close
fromCloseableResource
wasn't called, butafterAll
fromAfterAllCallback
was -
Steve K almost 4 yearsFWIW I implemented this with Spring to setup test data using a DataSource bean, and it worked nicely; details in stackoverflow.com/a/62504238/278800. I couldn't have done it without your helpful answer, thanks!
-
Andrew Lazarus over 3 yearsWorked first time, when combined with the information on auto-registering extensions.
-
dforce over 3 yearshow/why does it close only once? probably: junit.org/junit5/docs/current/user-guide/…
-
Stevel over 3 yearsI couldn't help myself and had to post a flamboyant "going a bit further" answer. Vote it up if you think it adds to the convo: stackoverflow.com/a/65450782/5957643
-
LeO over 3 yearsI extended the provided sample to be valid in parallel execution as well as the above mentioned automatic extension registration.
-
Ivan G. about 3 yearsOne unfortunate flaw with this approach: it is possible for other tests that don't share the extension to run between tests with the extension. Therefore, it may not be a suitable solution if the environment is shared in such a way that other tests running between break expectations
-
RoBeaToZ almost 3 yearsWorks perfect! Hint: The extension class has to be public, like in this example
-
Ryan Moser about 2 yearsShould probably have
unlock
in afinally
block and all the code in betweenlock
andunlock
in atry
-
LeO about 2 years@RyanMoser: Please correct/update it. Thx.
-
Ryan Moser about 2 years@LeO will do. The queue is apparently full ATM, so I have to wait? This is my first edit attempt lol