Java junit before all tests

In JUnit 5, how to run code before all tests

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.

Just for the record: in case you find one of the answers helpful enough — please consider accepting one of them at some point 😉

6 Answers 6

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:

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

If 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).

Читайте также:  Свойство padding bottom css

FWIW 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!

I 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

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.

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 exactly once // before the start of all test containers. abstract void setup(); // Callback that is invoked exactly once // after the end of all test containers. // Inherited from public abstract void close() throws Throwable; > 
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.

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.

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.

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() 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() 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"); >> 

Источник

Туториал по JUnit 5 — Аннотация @BeforeAll

Аннотация JUnit 5 помечает @BeforeAll метод, являющийся методом жизненного цикла JUnit 5 теста. @BeforeAll — это замена аннотации @BeforeClass в JUnit 4.

Это продолжение туториала по JUnit 5. Введение опубликовано здесь.

1. Аннотация @BeforeAll

Аннотация @BoforeAll используются для указания о том, что аннотированный метод должен быть выполнен перед всеми @Test , @RepeatedTest , @ParameterizedTest и @TestFactory методами в текущем классе.

По умолчанию тестовые методы будут выполняться в том же потоке, что и аннотированный @BeforeAll метод.

@BeforeAll public static void init()
  • Или мы можем применить эту аннотацию к default методам интерфейса, если тестовый интерфейс или тестовый класс аннотированы с помощью @TestInstance(Lifecycle.PER_CLASS).
@TestInstance(Lifecycle.PER_CLASS) interface TestLifecycleLogger < @BeforeAll default void beforeAllTests() < // >>

Если этого не сделать, JUnit выдаст ошибку времени выполнения типа JUnitException .

org.junit.platform.commons.JUnitException: @BeforeAll method 'public void com.howtodoinjava.junit5.examples. JUnit5AnnotationsExample.init()' must be static. at org.junit.jupiter.engine.descriptor. LifecycleMethodUtils.assertStatic(LifecycleMethodUtils.java:66)

@BeforeAll в родительском и дочернем классах

@BeforeAll методы наследуются от родительских классов (или интерфейсов), если они не скрыты или не переопределены.

Кроме того, @BeforeAll методы из родительских классов (или интерфейсов) будут выполняться перед @BeforeAll методами в дочерних классах.

2. Пример аннотации @BeforeAll

Рассмотрим пример. Мы написали тест для класса Calculator и его метода add() .

Выполним тест 5 раз с помощью аннотации @RepeatedTest . Аннотация @Repeated вызовет запуск теста add пять раз. Но аннотированный @BeforeAll метод нужно вызывать только один раз.

package com.howtodoinjava.junit5.examples; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepetitionInfo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; public class BeforeAllTest < @DisplayName("Add operation test") @RepeatedTest(5) void addNumber(TestInfo testInfo, RepetitionInfo repetitionInfo) < System.out.println("Running test ->" + repetitionInfo.getCurrentRepetition()); Assertions.assertEquals(2, Calculator.add(1, 1), "1 + 1 should equal 2"); > @BeforeAll public static void init() < System.out.println("Before All init() method called"); >>
package com.howtodoinjava.junit5.examples; public class Calculator < public static int add(int a, int b) < return a + b; >>

Теперь выполните тест, и вы увидите на консоли вывод:

Before All init() method called Running test -> 1 Running test -> 2 Running test -> 3 Running test -> 4 Running test -> 5

Понятно, что аннотированный @BeforeAll метод init() вызывается только один раз.

Источник

Оцените статью