在jUnit 4.x中执行Suite执行前后挂钩


84

我正在尝试使用jUnit 4.4执行一组集成测试的设置和拆卸。拆卸需要可靠地运行。我在使用TestNG时遇到其他问题,因此我希望移植回jUnit。在运行任何测试之前以及在完成所有测试之后,可以使用哪些挂钩执行?

注意:我们正在使用Maven 2进行构建。我已经尝试过使用maven的pre-post-integration-test阶段,但是,如果测试失败,则maven会停止并且不会运行post-integration-test,这没有帮助。


1
对于集成测试,您应该使用maven-failsafe-plugin而不是surefire。post-integration-test如果测试失败,则不会跳过。另请参阅此Wiki页面
克里斯H.

您能否分享您的最终实现?
vikramvi '18

Answers:


113

是的,在测试套件中进行任何测试之前和之后,都可以可靠地运行设置和拆卸方法。让我用代码演示:

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文档


2
一旦我进入了maven-surefire-plugin并创建了一个包含清单,该清单指向了我想要运行的套件,这对我来说非常理想。
Jherico

2
从JUnit 4.8.2开始,此方法不适用于参数化测试。Suite的@BeforeClass方法将在测试的@ Parameterized.Parameters方法之后运行,以防止对Suite设置的任何依赖。
ANM

在回答自己,用@Theories时,调用该方法@DataPoints是调用该软件套件的@BeforeClass。
ANM

18
抱歉,坏死了-但是将BeforeClass / AfterClass添加到超类无法按预期工作-在每个测试类完成后仍会调用它们。这是给后代的。
Subu Sankara Subramanian 2012年

3
这仍然是有效的方法吗?如何避免在SuiteClasses批注中枚举测试类列表?
Burhan Ali

33

我的一位同事提出了以下建议:您可以使用自定义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可以发挥其魔力,等待所有测试完成。


5
+1这是我在没有繁琐的Suites类维护的情况下见到的第一个可用解决方案!
Stefan Haberl 2013年


7

使用注释,您可以执行以下操作:

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()
    {

    }

}

4
设置和拆卸需要每次运行执行一次。仅当所有测试都在一个类中时,这才有帮助。
sblundy

3

在这里,我们

  • 升级到JUnit 4.5,
  • 编写注释以标记需要工作服务的每个测试类或方法,
  • 为每个注释编写了处理程序,其中包含静态方法以实现服务的设置和拆卸,
  • 扩展了通常的Runner以在测试上定位注释,并在适当的位置将静态处理程序方法添加到测试执行链中。

2

至于“注意:我们正在使用maven 2进行构建。我尝试使用maven的集成前和集成后测试阶段,但是,如果测试失败,则maven会停止并且不会运行集成后测试,这没有帮助。”

您可以尝试使用failsafe-plugin,我认为它具有确保无论设置或中间阶段状态如何都进行清除的功能


是的,failsafe插件将允许您指定特定的设置和拆卸。尽管我认为在发布此问题时不存在故障保险。
杰森·阿克森

2

假设您所有的测试都可以扩展“技术”类并且在同一包中,则可以做一些技巧:

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() {}
}

请注意,此解决方案有很多缺点:

  • 必须执行软件包的所有测试
  • 必须将“技术”类归类
  • 您不能在子类中使用@BeforeClass和@AfterClass
  • 如果您仅在包装中执行一项测试,则不进行清洁
  • ...

有关信息:listClassesIn()=>如何在Java中找到给定类的所有子类?


1
就我自己的测试而言,这是不正确的。我有一个超级类,可以在上课之前启动嵌入式glassfish,并在下课后关闭它。然后,我从该超类扩展了2个类。在运行每个类中定义的测试之前,将执行beforeclass。
乔纳森·莫拉莱斯·韦莱兹

0

据我所知,在JUnit中没有执行此操作的机制,但是您可以尝试对Suite进行子类化,并使用确实提供钩子的版本覆盖run()方法。


0

由于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>

0

我认为获得想要的功能的唯一方法是做类似的事情

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中使用类似的东西,所以我不确定它在该环境之外的便携性


3
这是JUnit3的示例,OP要求使用JUnit4,但以防万一某些JUnit3用户发现此问题...对于JUnit3,最好摆脱main()方法并使用suite()方法将TestSuite包装在junit.extensions.TestSetup的子类中。您仍然与朱莉(Julie)在IDE中运行各个测试类的示例相同。
NamshubWriter 2010年

0

如果您不想创建套件并必须列出所有测试类,则可以使用反射来动态查找测试类的数量,并在基类@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答案可能会满足您的要求。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.