当前位置: 首页 > 知识库问答 >
问题:

从带有@ClassRule的套件运行单个测试失败

单耘豪
2023-03-14

为了只创建一次环境并避免继承,我用一个类规则定义了一个JUnit套件类:

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

public class JUnitTest {

    @ClassRule
    private static DockerComposeContainer env = ...


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

    ...

}

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

public class SuiteTest1 {               

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


    }
}

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

关于如何以一种好的方式实现SuiteTest1单个执行的任何建议(无需从SuiteTest1中调用JUnitTest的静态方法)?

共有1个答案

艾令雪
2023-03-14

重新表述一下这个问题:您想要一个带有前后挂钩的JUnit套件,它在逐个运行测试时也会运行(例如从IDE运行)。

AFAIK JUnit 4没有提供任何现成的解决方案,但是如果您愿意将一些Spring第三方DEP(Spring测试和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"));
        }
    }
}

请注意SpringClassRurSpringmetodRurJUnit规则,它们使用Spring超能力增强了我们的基类(在这种情况下是Spring测试注释处理-ContextConfiguration,但其中还有更多好东西-有关详细信息,请参阅Spring测试参考)。为此,您可以使用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特定的日志以提高Brievity):

Initializing context
TestClass1 test
TestClass2 test
Closing context

运行单个测试时的输出(TestClass1):

Initializing context
TestClass1 test
Closing context

这种工作方式是因为Spring的上下文缓存。引用文档:

一旦TestContext框架为测试加载了Application Context(或WebApplication ationContext),该上下文就会被缓存并重用于在同一测试套件中声明相同唯一上下文配置的所有后续测试。要了解缓存的工作原理,重要的是要了解“唯一”和“测试套件”的含义。

-- https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/testing.html#testcontext-ctx管理缓存

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

您可以在上下文中定义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
 类似资料:
  • 我有一个用TestNG编写的大型测试套件,需要几个小时才能完成。 我想让maven/surefire创建一个新套件,它是第一个套件的副本,但只包含失败的测试,这个套件应该运行得更快。 有可能创建这样一个套件吗? 作为回退,如果有这样的报告,我可以从一个易于解析的测试报告中自己创建它。 非常感谢。

  • 我使用PyCharm来运行一个相当大的测试套件(1800个快速测试)。最近修改了一个测试,我意识到我的测试不会出错。当我单独运行这些测试时,确实会中断,但是如果我运行整个测试套件,它们总是会通过。这是由于一些以前的同事对类的这种不正确的嘲笑: 我现在需要检查整个测试套件中模拟的使用情况,但是我想单独运行每个测试,看看哪些测试没有通过(我将这些测试标记为高优先级)。我怎么能从控制台或Py魅力中做到这

  • 问题内容: 如何从命令行运行Junit 4.8.1测试套件?另外,我想使用JUnit 4.8引入的类别,有没有一种方法可以从命令行指定要运行的类别。 问题答案: 从4.8开始,无法从命令行指定类别。

  • 基于此链接中的答案,创建了一个包含测试类的测试套件 返回错误 但是每个添加的测试类都有测试方法,并且单独运行