java - 如何在多个测试类之间共享 JUnit BeforeClass 逻辑

标签 java unit-testing junit junit4

目前,我所有的 JUnit 测试都从一个公共(public)基类扩展而来,该基类提供了标记有 @BeforeClass@AfterClass 注释的方法 - 所有这些实际上都是设置一堆供测试使用的静态资源/服务。

这对我来说似乎很尴尬,原因如下:

  1. JUnit4 的部分观点(根据我的理解)是我们不再需要这种经典的测试继承。
  2. 当我将这些测试作为套件的一部分而不是单独运行(我们经常这样做)时,@BeforeClass@AfterClass 会被多次调用,速度变慢测试——我们真的应该只调用一次

我想做的是以某种方式将当前的 BeforeClass/AfterClass 逻辑从继承链中移出,并转移到可以由单个测试和整个套件共享的东西中。

可以这样做吗?如果是,怎么做?(如果重要的话,我使用的是 JUnit 4.7,更新到其他版本可能很难)

最佳答案

第一个问题的解决方案是将逻辑移动到通过 @ClassRule 连接到测试的 org.junit.rules.ExternalResource 的扩展中。 ,在 JUnit 4.9 中引入:

public class MyTest {
    @ClassRule
    public static final TestResources res = new TestResources();
    @Test
    public void testFoo() {
        // test logic here
    }
}

public class TestResources extends ExternalResource {
    protected void before() {
        // Setup logic that used to be in @BeforeClass
    }
    protected void after() {
        // Setup logic that used to be in @AfterClass
    }
}

通过这种方式,以前由基类管理的资源被移出测试类层次结构,进入更模块化/可消耗的“资源”,这些资源可以在类运行之前创建并在类运行后销毁。

至于同时解决这两个问题 - 即:作为单个测试的一部分和作为套件的一部分运行相同的高级设置/拆卸 - 似乎没有任何特定的内置对此的支持。 但是...您可以自己实现它:

只需将 @ClassRule 资源创建更改为在内部进行引用计数以确定是否创建/销毁资源的工厂模式。

例如(请注意这是粗略的,可能需要一些调整/错误处理以确保稳健性):

public class TestResources extends ExternalResource {
    private static int refCount = 0;

    private static TestResources currentInstance;

    public static TestResources getTestResources () {
        if (refCount == 0) {
            // currentInstance either hasn't been created yet, or after was called on it - create a new one
            currentInstance = new TestResources();
        }
        return currentInstance;
    }

    private TestResources() {
        System.out.println("TestResources construction");
        // setup any instance vars
    }

    protected void before() {
        System.out.println("TestResources before");
        try {
            if (refCount == 0) {
                System.out.println("Do actual TestResources init");
            }
        }
        finally {
            refCount++;
        }
    }

    protected void after() {
        System.out.println("TestResources after");
        refCount--;
        if (refCount == 0) {
            System.out.println("Do actual TestResources destroy");
        }
    }
}

您的套件/测试类都将通过工厂方法将资源用作 @ClassResource:

@RunWith(Suite.class)
@SuiteClasses({FooTest.class, BarTest.class})
public class MySuite {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();
    @BeforeClass
    public static void suiteSetup() {
        System.out.println("Suite setup");
    }
    @AfterClass
    public static void suiteTeardown() {
        System.out.println("Suite teardown");
    }
}
public class FooTest {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();

    @Test
    public void testFoo() {
        System.out.println("testFoo");
    }
}
public class BarTest {
    @ClassRule
    public static TestResources res = TestResources.getTestResources();

    @Test
    public void testBar() {
        System.out.println("testBar");
    }
}

运行单个测试时,引用计数不会产生任何影响 - “实际初始化”和“实际拆卸”只会发生一次。在运行套件时,套件将创建 TestResource,而各个测试将仅重用已实例化的测试资源(引用计数可防止它在套件中的测试之间被实际销毁和重新创建)。

关于java - 如何在多个测试类之间共享 JUnit BeforeClass 逻辑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27391055/

相关文章:

java - 单个文件中的子类型 Java?

java - 在不同类android中访问变量的最佳方法

angularjs - Angular 1.5 单元测试组件,同时忽略子组件

mysql - [ Play Framework ] : Performance testing with MySql

java - 如何使用 JUnit 设置全局规则

java - Gson 解析不带引号的值

java - Adobe Experience Manager 路径的完整 URL

c - 如何在C语言中使用cmake进行单元测试?

unit-testing - 可以在不更改任何代码的情况下对最初未设计的单元测试代码进行测试吗?

java - 如何在使用 ANT 运行类或套件中的所有测试时打印当前正在执行的 JUnit 测试方法?