【问题标题】:Does Junit reinitialize the class with each test method invocation?Junit 是否使用每个测试方法调用重新初始化类?
【发布时间】:2013-10-15 12:33:09
【问题描述】:

当我运行下面的代码时,两个测试用例都实现了:

import static junit.framework.Assert.assertEquals;

import org.junit.Test;

public class MyTest{
    private int count;

    @Before
    public void before(){
        count=1;
    }

    @Test
    public void test1(){
        count++;
        assertEquals(2, count); 
    }

    @Test
    public void test2(){
        count++;
        assertEquals(2, count); 
    }
}

预期行为

  1. test1 - 成功
  2. test2 - 失败(按预期计数将变为 3)

实际行为

  1. test1 - 成功
  2. test2 - 成功

为什么 junit 对于每个测试方法调用都是reinitializing class/variable。 这是junit中的一个错误或者是故意提供的。

【问题讨论】:

    标签: java unit-testing junit junit4 junit3


    【解决方案1】:

    这是因为测试隔离。

    任何测试都不应该依赖于另一个测试。

    【讨论】:

    • 在这个例子中,它们不相互依赖
    • @Sean Owen 是的,但是如果 junit 会为每个测试方法重用 MyTest 实例,那么测试可能会相互影响。这就是 junit 为每个测试方法创建一个新的 MyTest 实例的原因。如果测试可以相互影响,那么很难找到失败的原因,不是吗?
    • 我完全同意让测试独立。我认为问题更多是关于如何正确实现它,但我现在不太确定,所以这实际上可能是重点。
    【解决方案2】:

    每个测试方法的MyTest的新实例

    对于每个测试方法,将创建一个MyTest新实例,这是 Junit 的行为。

    因此,对于这两种方法,变量 count 的值将是 1,因此对于两种测试方法,count++ 的值将是 2,因此测试用例通过。

    public class MyTest{
       public MyTest(){
          // called n times
          System.out.println("Constructor called for MyTest");
       }
    
       @Before //called n times
       public void setUp(){
          System.out.println("Before called for MyTest");
       }
        
       //n test methods
    }
    

    如果你用 2 种测试方法执行上面的代码:

    输出将是:

    Constructor called for MyTest
    Before called for MyTest
    //test execution
    Constructor called for MyTest
    Before called for MyTest
    

    编辑:

    从 F.I.R.S.T 测试原则中隔离

    测试框架帮助您做正确的事,单元测​​试的一个非常重要的属性是隔离

    通过为每个测试方法创建一个新实例,脏 SUT 被丢弃。这样我们每次测试都有一个新的状态。

    了解 F.I.R.S.T 测试原理。

    【讨论】:

      【解决方案3】:

      org.junit.runner.Runner的文档:

      默认的运行器实现保证 测试用例类将在运行之前立即构建 测试并且运行者将不保留对测试用例的引用 实例,通常使它们可用于垃圾回收。

      单元测试应该是独立的,否则它变得不可维护。注意,执行方法的顺序是不能保证的(除非你使用注解@FixMethodOrder)。

      【讨论】:

        【解决方案4】:

        JUnit 5 的答案

        在 JUnit5 中,此行为由 @TestInstance 注释控制。注释可以取两个生命周期限定符之一作为值:

        • @TestInstance(Lifecycle.PER_CLASS):测试类会为类中的所有方法初始化一次。
        • @TestInstance(Lifecycle.PER_METHOD):测试类将在每个测试方法之前重新初始化(其他答案中描述的行为)。

        如果一个测试类没有用@TestInstance注解,默认行为是PER_METHOD

        有关详细信息,请参阅 JUnit5 用户指南中的Test Instance Lifecycle

        【讨论】:

          【解决方案5】:

          如果您想对所有测试使用测试类的成员变量,而不是将其重新初始化为 null,则将其设为 静态

          【讨论】:

          • 我认为这是更多的评论。
          • 我猜,Stackoverflow 中的声誉不足以添加 cmets :)(但显然足以添加答案!惊喜)
          【解决方案6】:

          除非它是不可变的,否则不要在构造函数中初始化测试类状态。

          JUnit 不会为每个@Test 实例化您的测试类。事实上,它只在每个方法之前运行标记为@Before 的方法,并且会在类中的所有测试之前运行一次@BeforeClass 方法。

          但是,您不能保证测试运行程序实际上只会使用您的测试类的一个实例来运行测试。它可以免费使用很多——考虑并行运行一堆测试,甚至在不同的机器上。

          虽然通常有 JUnit 运行器设置来控制这一点,但最好简单地遵循 JUnit 设计并在标记为 @Before 的方法中初始化测试状态。

          【讨论】:

          • 你的话自相矛盾。在大多数情况下,JUnit“确实”为每个 @Test 实例化测试类,不是吗?
          猜你喜欢
          • 2018-12-06
          • 1970-01-01
          • 2018-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多