在JUnit 5中,如何在所有测试之前运行代码

Rob*_*b N 25 junit junit5

@BeforeAll注释标记在所有测试之前运行的方法.

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

但是在所有类中,有没有办法在所有测试之前运行一些代码?

我想确保测试使用一组特定的数据库连接,并且运行任何测试之前必须进行这些连接的全局一次性设置.

Phi*_*ret 31

现在可以通过创建自定义扩展在JUnit5中实现,您可以从根扩展中注册关闭挂钩.

你的扩展名看起来像这样;

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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要执行至少一次的任何测试类都可以使用以下注释进行注释:

@ExtendWith({YourExtension.class})
Run Code Online (Sandbox Code Playgroud)

在多个类上使用此扩展时,启动和关闭逻辑将仅调用一次.

  • 如果您需要为每个测试类执行此操作,那么您还可以使用 [自动扩展注册](https://junit.org/junit5/docs/current/user-guide/#extensions-registration-automatic) 来注册使用 ServiceLoader 进行扩展。然后你不需要注释每一个测试(有可能错过一些的风险)。 (3认同)
  • 在我的例子中,“CloseableResource”中的“close”未被调用,但“AfterAllCallback”中的“afterAll”被调用 (3认同)
  • 您应该使用“getOrComputeIfAbsent”来避免使用静态变量“started”并使其线程安全。 (2认同)

LeO*_*LeO 10

@Philipp Gayret已经提供的答案在并行测试 JUnit 时存在一些问题(即junit.jupiter.execution.parallel.enabled = true)。

因此,我将解决方案调整为:

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 {

    private static boolean started = false;
    // Gate keeper to prevent multiple Threads within the same routine
    final static Lock lock = new ReentrantLock();

    @Override
    public void beforeAll(final ExtensionContext context) throws Exception {
        // lock the access so only one Thread has access to it
        lock.lock();
        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
        }
        // free the access
        lock.unlock();
    }

    @Override
    public void close() {
        // Your "after all tests" logic goes here
    }
}
Run Code Online (Sandbox Code Playgroud)

如下所述,JUnit5 提供了一个自动扩展注册。为此,在src/test/resources/名为 的目录中/META-INF/services添加 并添加名为 的文件org.junit.jupiter.api.extension.Extension。将您班级的完全分类名称添加到此文件中,例如

at.myPackage.BeforeAllTestsExtension
Run Code Online (Sandbox Code Playgroud)

接下来在同一个 Junit 配置文件中启用

junit.jupiter.extensions.autodetection.enabled=true
Run Code Online (Sandbox Code Playgroud)

有了这个扩展程序会自动附加到您的所有测试中。


Ste*_*ner 5

当前不支持此功能,但是有一个关于此主题的对JUnit 5的请求:在整个测试运行中引入一次对before / after回调的支持


mfu*_*n26 5

您可以使用定义 a 的接口标记使用您的数据库的每个测试类static BeforeAll(以便它不能被覆盖)。例如:

interface UsesDatabase {
    @BeforeAll
    static void initializeDatabaseConnections() {
        // initialize database connections
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法将为每个实现类调用一次,因此您需要定义一种方法来初始化您的连接一次,然后对其他调用不做任何事情。


小智 5

根据@Philipp 的建议,这里有一个更完整的代码片段:

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;
}
Run Code Online (Sandbox Code Playgroud)

如何使用:

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 { ... }
}
Run Code Online (Sandbox Code Playgroud)

测试执行顺序为:

  DemoSetupExtension.setup (*)
  TestOne.beforeAllTestOne
  TestOne.testOne
  TestOne.afterAllTestOne
  TestTwo.beforeAllTestTwo
  TestTwo.testTwo
  TestTwo.afterAllTestTwo
  DemoSetupExtension.close (*)
Run Code Online (Sandbox Code Playgroud)

...无论您选择运行单个@Test(例如TestOne.testOne),还是整个测试类(TestOne),或多个/所有测试,这都是正确的。


Gho*_*ica 2

我不知道有什么办法可以做到这一点。

我只是确保 @BeforeAll 的所有代码都调用某个单例以使 init 工作(可能以一种懒惰的方式避免重复)。

可能不方便......我看到的唯一其他选项:我假设您的测试在特定的 JVM 作业中运行。您可以将代理挂接到该 JVM 运行中,这样 init 就会为您工作。

除此之外:这两个建议对我来说听起来都像是黑客。我眼中真正答案是:退后一步,仔细检查您的环境的依赖性。然后找到一种方法来准备您的环境,以便您的测试出现并且“正确的事情”自动发生。换句话说:考虑研究一下给你带来这个问题的架构。