java 如何在多个测试类之间共享 JUnit BeforeClass 逻辑
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27391055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to share JUnit BeforeClass logic among multiple test classes
提问by Krease
Currently, all of my JUnit tests extend from a common base class that provides methods tagged with @BeforeClass
and @AfterClass
annotations - all these really do is setup a bunch of static resources/services for the tests to use.
目前,我所有的 JUnit 测试都从一个公共基类扩展而来,该基类提供了带有标记@BeforeClass
和@AfterClass
注释的方法 ——所有这些真正做的就是设置一堆静态资源/服务供测试使用。
This seems a awkward to me for a few reasons:
由于以下几个原因,这对我来说似乎很尴尬:
- Part of the point of JUnit4 (from my understanding) is that we shouldn't need this classical test inheritance anymore.
- When I run these tests as part of a suite instead of individually (which we often do), the
@BeforeClass
and@AfterClass
get invoked multiple times, slowing down the tests - we really should only be calling these once
- JUnit4 的部分观点(根据我的理解)是我们不再需要这种经典的测试继承。
- 当我将这些测试作为套件的一部分而不是单独运行(我们经常这样做)时,
@BeforeClass
和@AfterClass
被多次调用,减慢了测试速度 - 我们真的应该只调用一次
What I'd like to do is somehow move the current BeforeClass/AfterClass logic out of the inheritance chain and into something that can be shared by individual tests and the suite as a whole.
我想要做的是以某种方式将当前的 BeforeClass/AfterClass 逻辑移出继承链,并进入可以由单个测试和整个套件共享的东西。
Can this be done? If so, how?(If it matters, I'm using JUnit 4.7, and it could be a hard sell to update to a different version)
这能做到吗?如果是这样,如何?(如果重要的话,我使用的是 JUnit 4.7,更新到不同版本可能很难)
回答by Krease
A solution to the first issue is to move the logic into an extension of org.junit.rules.ExternalResource
hooked up to the test via a @ClassRule
, introduced in JUnit 4.9:
第一个问题的解决方案是将逻辑移动到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
}
}
In this way, the resources previously managed by the base class are moved out of the test class hierarchy and into more modular/consumable "resources" that can be created before a class runs and destroyed after a class runs.
通过这种方式,先前由基类管理的资源从测试类层次结构中移出,并移入更模块化/可消耗的“资源”中,这些“资源”可以在类运行之前创建并在类运行之后销毁。
As for solving both issues at the same time though - ie: having the same high level setup/teardown run as both part of an individual test and as part of a suite - there doesn't seem to be any specific built in support for this. However..., you could implement it yourself:
至于同时解决这两个问题 - 即:作为单独测试的一部分和作为套件的一部分运行相同的高级设置/拆卸 - 似乎没有任何特定的内置支持. 但是...,您可以自己实现它:
Simply change the @ClassRule
resource creation into a factory pattern that does reference counting internally to determine whether or not to create/destroy the resource.
只需将@ClassRule
资源创建更改为在内部进行引用计数的工厂模式,以确定是否创建/销毁资源。
For example (please note this is rough and might need some tweaks/error handling for robustness):
例如(请注意,这很粗糙,可能需要一些调整/错误处理以确保稳健性):
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");
}
}
}
Both your suite / test classes would just use the resource as a @ClassResource
through the factory method:
您的套件/测试类都将@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");
}
}
When running an individual test, the refcounting won't have any effect - the "actual init" and "actual teardown" will only happen once. When running through the suite, the suite will create the TestResource, and the individual tests will just reuse the already instantated one (the refcounting keeps it from being actually destroyed and recreated between tests in the suite).
运行单个测试时,引用计数不会产生任何影响——“实际初始化”和“实际拆卸”只会发生一次。在套件中运行时,套件将创建 TestResource,并且各个测试将仅重用已经实例化的测试资源(引用计数防止它在套件中的测试之间被实际销毁和重新创建)。
回答by dkatzel
You can use the @BeforeClass
and @AfterClass
IN THE SUITE CLASS.
您可以在 SUITE 类中使用@BeforeClass
和。@AfterClass
This will run the methods before any of the test classes in the suite execute and after all the test classes finish (respectively)
这将在套件中的任何测试类执行之前和所有测试类完成之后(分别)运行这些方法
This way you can run them only once.
这样,您只能运行它们一次。
//..usual @RunWith etc annotations here
public class MySuite{
@BeforeClass
public static void setup(){
}
@AfterClass
public static void tearDown(){
}
}
回答by Eanlr
I came across similar problem (Spring was not an option and i don't write TestSuites in maven projects) so i wrote simple junit runner to solve this problem.
我遇到了类似的问题(Spring 不是一个选项,我不在 maven 项目中编写 TestSuites)所以我编写了简单的 junit runner 来解决这个问题。
You need to write SharedResource class and mark your test to require that resource.
您需要编写 SharedResource 类并将您的测试标记为需要该资源。
public class SampleSharedResource implements SharedResource {
public void initialize() throws Exception {
//init your resource
}
}
}
@RunWith(JUnitSharedResourceRunner.class)
@JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class})
public class SharedResourceRunnerATest {
...
Sources at https://github.com/eanlr/junit-shared-resources-runner