gpt4 book ai didi

java - 从带有 @ClassRule 的 Suite 运行单个测试失败

转载 作者:太空宇宙 更新时间:2023-11-04 10:09:06 24 4
gpt4 key购买 nike

为了只创建一次环境并避免继承,我使用 @ClassRule 定义了一个 JUnit Suite 类:

@RunWith(Suite.class)               
@Suite.SuiteClasses({
SuiteTest1.class
})

public class JUnitTest {

@ClassRule
private static DockerComposeContainer env = ...


@BeforeClass
public static void init(){
...
}

...

}

还有一个 Test 类在测试方法中使用 env:

public class SuiteTest1 {               

@Test
public void method(){
client.query(...);// Executes a query against docker container


}
}

当我通过运行测试套件来执行测试时,一切都按预期进行。但是,当我直接尝试运行(即使使用 IDE)SuiteTest1 测试类时,它会失败,并且不会调用 Suite 中的任何内容(即 @ ClassRule@BeforeClass)。

关于如何以良好的方式实现 SuiteTest1 单一执行(无需从 SuiteTest1 中调用 JUnitTest 的静态方法)有什么建议吗?

最佳答案

重新表述问题:您需要一个带有 before-all 和 after-all Hook 的 JUnit 套件,它也会在逐个运行测试时运行(例如从 IDE)。

据我所知,JUnit 4 没有为此提供任何开箱即用的功能,但如果您愿意将一些 Spring 第三方 deps( spring-testspring-context )合并到您的项目中,我可以提出一个我一直在使用的解决方法。

本文后续内容的完整代码示例 can be found here .

解决方案(使用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"));
}
}
}

注意 SpringClassRuleSpringMethodRule JUnit 规则使用 Spring super 能力增强我们的基类(Spring 测试注释处理 - 在本例中为 ContextConfiguration,但其中还有更多好东西 - 有关详细信息,请参阅 Spring testing reference)。您可以使用 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 的上下文缓存。引用文档:

Once the TestContext framework loads an ApplicationContext (or WebApplicationContext) for a test, that context is cached and reused for all subsequent tests that declare the same unique context configuration within the same test suite. To understand how caching works, it is important to understand what is meant by “unique” and “test suite.”

-- https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/testing.html#testcontext-ctx-management-caching

请注意,如果您为层次结构中的任何类(在我们的示例中为 TestClass1TestClass2)重写上下文配置(例如,使用 ContextConfiguration 添加另一个上下文初始值设定项),您将获得另一个上下文(和另一个初始化)。

使用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());
}
}
}

并将其注入(inject)到测试类中:

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

关于java - 从带有 @ClassRule 的 Suite 运行单个测试失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52551735/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com