我正在尝试使用jUnit 4.4执行一组集成测试的设置和拆卸。拆卸需要可靠地运行。我在使用TestNG时遇到其他问题,因此我希望移植回jUnit。在运行任何测试之前以及在完成所有测试之后,可以使用哪些挂钩执行?
注意:我们正在使用Maven 2进行构建。我已经尝试过使用maven的pre-
&post-integration-test
阶段,但是,如果测试失败,则maven会停止并且不会运行post-integration-test
,这没有帮助。
我正在尝试使用jUnit 4.4执行一组集成测试的设置和拆卸。拆卸需要可靠地运行。我在使用TestNG时遇到其他问题,因此我希望移植回jUnit。在运行任何测试之前以及在完成所有测试之后,可以使用哪些挂钩执行?
注意:我们正在使用Maven 2进行构建。我已经尝试过使用maven的pre-
&post-integration-test
阶段,但是,如果测试失败,则maven会停止并且不会运行post-integration-test
,这没有帮助。
Answers:
是的,在测试套件中进行任何测试之前和之后,都可以可靠地运行设置和拆卸方法。让我用代码演示:
package com.test;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {
@BeforeClass
public static void setUp() {
System.out.println("setting up");
}
@AfterClass
public static void tearDown() {
System.out.println("tearing down");
}
}
所以您的Test1
课程看起来像这样:
package com.test;
import org.junit.Test;
public class Test1 {
@Test
public void test1() {
System.out.println("test1");
}
}
...您可以想象Test2
看起来很像。如果您运行TestSuite
,则会得到:
setting up
test1
test2
tearing down
因此,您可以看到设置/拆卸仅分别在所有测试之前和之后运行。
要点:仅当您运行测试套件且未将Test1和Test2作为单独的JUnit测试运行时,此方法才有效。您提到您正在使用maven,并且maven surefire插件喜欢单独运行测试,而不是套件的一部分。在这种情况下,我建议创建一个每个测试类都可以扩展的超类。然后,超类包含带注释的@BeforeClass和@AfterClass方法。尽管不如上述方法那么干净,但我认为它会为您工作。
至于失败测试的问题,您可以设置maven.test.error.ignore,以便在失败测试中继续构建。不建议您将其作为持续的实践,但是在所有测试通过之前,它应该可以使您正常运行。有关更多详细信息,请参见maven surefire文档。
我的一位同事提出了以下建议:您可以使用自定义RunListener并实现testRunFinished()方法:http ://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org 。 junit.runner.Result)
要注册RunListener,只需按以下方式配置surefire插件:http ://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html 部分“使用自定义侦听器和报告器”
故障安全插件也应选择此配置。这个解决方案很棒,因为您不必指定套件,查找测试类或任何此类东西-它使Maven可以发挥其魔力,等待所有测试完成。
使用注释,您可以执行以下操作:
import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;
class SomethingUnitTest {
@BeforeClass
public static void runBeforeClass()
{
}
@AfterClass
public static void runAfterClass()
{
}
@Before
public void setUp()
{
}
@After
public void tearDown()
{
}
@Test
public void testSomethingOrOther()
{
}
}
在这里,我们
假设您所有的测试都可以扩展“技术”类并且在同一包中,则可以做一些技巧:
public class AbstractTest {
private static int nbTests = listClassesIn(<package>).size();
private static int curTest = 0;
@BeforeClass
public static void incCurTest() { curTest++; }
@AfterClass
public static void closeTestSuite() {
if (curTest == nbTests) { /*cleaning*/ }
}
}
public class Test1 extends AbstractTest {
@Test
public void check() {}
}
public class Test2 extends AbstractTest {
@Test
public void check() {}
}
请注意,此解决方案有很多缺点:
有关信息:listClassesIn()=>如何在Java中找到给定类的所有子类?
由于maven-surefire-plugin不会先运行套件类,而是将套件和测试类视为相同,因此我们可以按以下方式配置插件,以仅启用套件类并禁用所有测试。Suite将运行所有测试。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<includes>
<include>**/*Suite.java</include>
</includes>
<excludes>
<exclude>**/*Test.java</exclude>
<exclude>**/*Tests.java</exclude>
</excludes>
</configuration>
</plugin>
我认为获得想要的功能的唯一方法是做类似的事情
import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("TestEverything");
//$JUnit-BEGIN$
suite.addTestSuite(TestOne.class);
suite.addTestSuite(TestTwo.class);
suite.addTestSuite(TestThree.class);
//$JUnit-END$
}
public static void main(String[] args)
{
AllTests test = new AllTests();
Test testCase = test.suite();
TestResult result = new TestResult();
setUp();
testCase.run(result);
tearDown();
}
public void setUp() {}
public void tearDown() {}
}
我在eclipse中使用类似的东西,所以我不确定它在该环境之外的便携性
如果您不想创建套件并必须列出所有测试类,则可以使用反射来动态查找测试类的数量,并在基类@AfterClass中递减计数,仅执行一次一次tearDown:
public class BaseTestClass
{
private static int testClassToRun = 0;
// Counting the classes to run so that we can do the tear down only once
static {
try {
Field field = ClassLoader.class.getDeclaredField("classes");
field.setAccessible(true);
@SuppressWarnings({ "unchecked", "rawtypes" })
Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
for (Class<?> clazz : classes) {
if (clazz.getName().endsWith("Test")) {
testClassToRun++;
}
}
} catch (Exception ignore) {
}
}
// Setup that needs to be done only once
static {
// one time set up
}
@AfterClass
public static void baseTearDown() throws Exception
{
if (--testClassToRun == 0) {
// one time clean up
}
}
}
如果您更喜欢使用@BeforeClass而不是静态块,则还可以使用布尔标志来进行反射计数并在第一次调用时仅测试一次设置。希望这对某人有帮助,我花了一个下午才找到一种比枚举套件中所有课程更好的方法。
现在,您需要做的就是为所有测试类扩展此类。我们已经有一个基类为所有测试提供一些通用的东西,因此这对我们来说是最佳的解决方案。
灵感来自这个SO答案https://stackoverflow.com/a/37488620/5930242
如果您不想将此类扩展到任何地方,那么最后一个SO答案可能会满足您的要求。
post-integration-test
如果测试失败,则不会跳过。另请参阅此Wiki页面。