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

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

由于以下几个原因,这对我来说似乎很尴尬:

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

我想要做的是以某种方式将当前的BeforeClass / AfterClass逻辑移出inheritance链,并将其转换为可以由各个测试和整个套件共享的内容。

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

第一个问题的解决方案是将逻辑移动到org.junit.rules.ExternalResource的扩展,通过@ClassRule连接到测试,在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资源创建更改为工厂模式,该模式在内部引用计数以确定是否创建/销毁资源。

例如(请注意这是粗略的,可能需要一些调整/error handling的稳健性):

 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,并且各个测试将仅重用已经过时的测试(refcounting使其不会被实际销毁并在套件中的测试之间重新创建)。

您可以在SUITE CLASS中使用@BeforeClass@AfterClass

这将在套件中的任何测试类执行之前运行方法,并且在所有测试类完成之后(分别)

这样您只能运行一次。

 //..usual @RunWith etc annotations here public class MySuite{ @BeforeClass public static void setup(){ } @AfterClass public static void tearDown(){ } } 

我遇到了类似的问题(Spring不是一个选项,我不在maven项目中编写TestSuites)所以我写了简单的junit runner来解决这个问题。

您需要编写SharedResource类并将测试标记为需要该资源。

 public class SampleSharedResource implements SharedResource { public void initialize() throws Exception { //init your resource } 

}

 @RunWith(JUnitSharedResourceRunner.class) @JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class}) public class SharedResourceRunnerATest { ... 

来源: https://github.com/eanlr/junit-shared-resources-runner