JUnit 5: Inject spring components to Extension (BeforeAllCallback / AfterAllCallback)

时光毁灭记忆、已成空白 提交于 2020-07-03 08:13:25

问题


tl;dr: How can I instantiate a custom data provider as a Spring component before all tests run?

Is there a smart way to inject Spring components into a custom JUnit Jupiter extension that implements BeforeAllCallback? The beforeAll method should trigger a complex process before MyTestClass is executed with @ExtendWith(OncePerTestRunExtension.class).

I created a Spring Boot Application (src/main/java) that provides my test (src/test/java) with the necessary data. The data can take up to a few hours to be prepared for the tests. It also gives me abstracted access to some rest-endpoints.

The data does not change in between the process of all test classes. So I just want to pull the data once.

Writing all tests in just one class would work, but I think the separation into different classes gives a better overview.


回答1:


In the beforeAll(ExtensionContext) method of your custom BeforeAllCallback, you can access the Spring ApplicationContext for the current test class via SpringExtension.getApplicationContext(extensionContext).

If you configure your custom data provider as a Spring component in that ApplicationContext, you can then retrieve the component from the ApplicationContext within your extension -- for example, via applicationContext.getBean(MyDataProvider.class).

If you need to process the data and store that processed data between tests, you can store that in the root ExtensionContext.Store in JUnit Jupiter. See ExtensionContext.getRoot() and the getOrComputeIfAbsent(...) variants in ExtensionContext.Store for details.




回答2:


Here's how I implemented this to setup some test data in my database using a dataSource Spring bean, by combining Sam's answer above with this answer: https://stackoverflow.com/a/51556718/278800

import javax.sql.DataSource;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;

public class TestDataSetup implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {

  private static boolean started = false;

  private DataSource dataSource;

  @Override
  public void beforeAll(ExtensionContext extensionContext) {
    synchronized (TestDataSetup.class) {
      if (!started) {
        started = true;

        // get the dataSource bean from the spring context
        ApplicationContext springContext = SpringExtension.getApplicationContext(extensionContext);
        this.dataSource = springContext.getBean(DataSource.class);

        // TODO: put your one-time db initialization code here

        // register a callback hook for when the root test context is shut down
        extensionContext
            .getRoot()
            .getStore(ExtensionContext.Namespace.GLOBAL)
            .put("TestDataSetup-started", this);
      }
    }
  }

  @Override
  public void close() {
    synchronized (TestDataSetup.class) {
      // TODO: put your db cleanup code here
    }
  }

(I'm not 100% sure on the thread safety of this, so I added the synchronized block just to be safe.)

To enable this extension, you just need to add this annotation to your test classes which need it:

@ExtendWith(TestDataSetup.class)

The nice thing is that Junit 5 allows multiple extensions, so this works even if your tests are already annotated with @ExtendWith(SpringExtension.class).



来源:https://stackoverflow.com/questions/56904620/junit-5-inject-spring-components-to-extension-beforeallcallback-afterallcall

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!