重新表述这个问题:您想要一个带有 before-all 和 after-all 钩子的 JUnit 套件,它也将在逐个运行测试时运行(例如,从 IDE 运行)。
AFAIK JUnit 4 没有为此提供任何开箱即用的功能,但是如果您可以将一些 Spring 第三方部门(spring-test和spring-context)合并到您的项目中,我可以提出一个解决方法使用。
可以在此处找到本文后面的完整代码示例。
解决方案(使用 Spring)
我们将使用 Spring 上下文来实现我们的初始化和清理。让我们为我们的测试添加一个基类:
@ContextConfiguration(initializers = AbstractTestClass.ContextInitializer.class)
public class AbstractTestClass {
@ClassRule
public final static SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
public static class ContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("Initializing context");
context.addApplicationListener(
(ApplicationListener<ContextClosedEvent>)
contextClosedEvent ->
System.out.println("Closing context"));
}
}
}
请注意使用 Spring-superpowers 增强我们的基类的 JUnit 规则(Spring 测试注释处理 -SpringClassRule
在这种情况下,但还有更多好东西 - 请参阅Spring 测试参考了解详细信息)。您可以将其用于此目的,但它的灵活性要低得多(因此省略)。SpringMethodRule
ContextConfiguration
SpringRunner
测试类:
public class TestClass1 extends AbstractTestClass {
@Test
public void test() {
System.out.println("TestClass1 test");
}
}
public class TestClass2 extends AbstractTestClass {
@Test
public void test() {
System.out.println("TestClass2 test");
}
}
和测试套件:
@RunWith(Suite.class)
@SuiteClasses({TestClass1.class, TestClass2.class})
public class TestSuite {
}
运行套件时的输出(为简洁起见,删除了特定于 Spring 的日志):
Initializing context
TestClass1 test
TestClass2 test
Closing context
运行单个测试 ( TestClass1
) 时的输出:
Initializing context
TestClass1 test
Closing context
一句话解释
这种工作方式是因为 Spring 的上下文缓存。从文档中引用:
一旦 TestContext 框架为一个测试加载了一个ApplicationContext
(or WebApplicationContext
),该上下文就会被缓存并重用于在同一测试套件中声明相同唯一上下文配置的所有后续测试。要了解缓存的工作原理,请务必了解“唯一”和“测试套件”的含义。</p>
-- https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/testing.html#testcontext-ctx-management-caching
ContextConfiguration
请注意,如果您为层次结构中的任何类(TestClass1
或TestClass2
在我们的示例中)覆盖上下文配置(例如添加另一个上下文初始化器),您将获得另一个上下文(和另一个初始化)。
使用 bean 共享实例
您可以在上下文中定义 bean。它们将在使用相同上下文的所有测试中共享。这对于在测试套件中共享对象(根据标签判断为您的案例中的 Testcontainers 容器)非常有用。
让我们添加一个 bean:
@ContextConfiguration(initializers = AbstractTestClass.ContextInitializer.class)
public class AbstractTestClass {
@ClassRule
public final static SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
public static class ContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
ADockerContainer aDockerContainer = new ADockerContainer();
aDockerContainer.start();
context.getBeanFactory().registerResolvableDependency(
ADockerContainer.class, aDockerContainer);
context.addApplicationListener(
(ApplicationListener<ContextClosedEvent>)
contextClosedEvent ->
aDockerContainer.stop());
}
}
}
并将其注入测试类:
public class TestClass1 extends AbstractTestClass {
@Autowired
private ADockerContainer aDockerContainer;
@Test
public void test() {
System.out.println("TestClass1 test " + aDockerContainer.getData());
}
}
public class TestClass2 extends AbstractTestClass {
@Autowired
private ADockerContainer aDockerContainer;
@Test
public void test() {
System.out.println("TestClass2 test " + aDockerContainer.getData());
}
}
ADockerContainer
班级:
public class ADockerContainer {
private UUID data;
public void start() {
System.out.println("Start container");
data = UUID.randomUUID();
}
public void stop() {
System.out.println("Stop container");
}
public String getData() {
return data.toString();
}
}
(示例)输出:
Start container
TestClass1 test 56ead80b-ec34-4dd6-9c0d-d6f07a4eb0d8
TestClass2 test 56ead80b-ec34-4dd6-9c0d-d6f07a4eb0d8
Stop container