问题
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