使用@ClassRule从套件运行单个测试失败

时间:2018-09-28 08:56:24

标签: java junit junit4 testcontainers

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

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

public class JUnitTest {

    @ClassRule
    private static DockerComposeContainer env = ...


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

    ...

}

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

public class SuiteTest1 {               

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


    }
}

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

关于如何以良好的方式也实现SuiteTest1单执行的任何建议(无需从JUnitTest内部调用SuiteTest1的静态方法)?

1 个答案:

答案 0 :(得分:0)

表述这个问题:您想要一个带有所有之前和之后挂钩的JUnit套件,当逐个运行测试时(例如从IDE中),该套件也会运行。

AFAIK JUnit 4对此不提供任何现成的功能,但是如果您可以将一些Spring第三方部门(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-superpowers增强了我们的基类(在这种情况下,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的上下文缓存。引用文档:

  

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

     

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

请注意,如果您为层次结构(ContextConfiguration或{中的任何类覆盖上下文配置(例如,使用TestClass1添加另一个上下文初始化器),您将获得另一个上下文(以及另一个初始化)。在我们的示例中为{1}}。

使用bean共享实例

您可以在上下文中定义bean。它们将在使用相同上下文的所有测试中共享。这对于在整个测试套件中共享对象很有用(在您的案例中,根据标签来确定一个Testcontainers容器)。

我们添加一个bean:

TestClass2

并将其注入测试类:

@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();
    }
}
相关问题