我正在尝试为一组集成测试进行设置和拆卸,使用 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
,这无济于事。
是的,可以在测试套件中的任何测试之前和之后可靠地运行设置和拆除方法。让我用代码演示一下:
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 发挥它的魔力,等待所有测试完成。
Using annotations, you can do something like this:
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 2 进行构建。我尝试过使用 maven 的 pre- & post-integration-test 阶段,但是,如果测试失败,maven 会停止并且不会运行 post-integration-test ,这无济于事。”
您可以尝试使用故障安全插件,我认为它可以确保无论设置或中间阶段状态如何都可以进行清理
我认为获得所需功能的唯一方法是执行类似的操作
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中使用了类似的东西,所以我不确定它在那个环境之外的便携性
As far as I know there is no mechanism for doing this in JUnit, however you could try subclassing Suite and overriding the run() method with a version that does provide hooks.
由于 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>
如果您不想创建套件并且必须列出所有测试类,则可以使用反射来动态查找测试类的数量并在基类 @AfterClass 中倒计时以仅进行一次拆卸:
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 答案可能会满足您的需求。