JUnit:使用构造函数代替@Before

ejk8hzay  于 2023-10-20  发布在  其他
关注(0)|答案(8)|浏览(145)

我用的是JUnit 4。我看不出在构造函数中初始化和使用@Before注解的专用init函数之间的区别。是不是意味着我不用担心了?
@Before在构造函数中给出的不仅仅是初始化,还有其他情况吗?

holgip5t

holgip5t1#

不,使用构造函数初始化JUnit测试夹具在技术上等同于使用@Before方法(因为JUnit为每个@Test创建了测试类的新示例)。唯一的(内涵上的)区别是它打破了@Before@After之间的对称性,这可能会让一些人感到困惑。IMHO最好遵守约定(使用@Before)。
还要注意的是,在JUnit 4和annotations之前,有专门的setUp()tearDown()方法-@Before@After annotations替换了这些方法,但保留了底层逻辑。因此,使用注解也使从JUnit 3或更早版本迁移的人更容易。

显著差异

评论中的更多细节:

  • @Before允许重写父类行为,构造函数强制您调用父类构造函数
  • 构造函数在子类构造函数和@Rule方法之前运行,@Before在所有这些方法之后运行
  • @Before期间调用会导致调用@After方法,但在构造函数中调用不会
gjmwrych

gjmwrych2#

@Before在某些情况下使用更有意义,因为它在类的构造函数之后被调用。当您使用带有@Mock注解的Mockito这样的mock框架时,这种差异很重要,因为您的@Before方法将在mock初始化之后被调用。然后,您可以使用模拟为被测类提供构造函数参数。
我发现在使用协作bean时,这是我的单元测试中非常常见的模式。
这里有一个(当然是人为的)例子:

@RunWith(MockitoJUnitRunner.class)
public class CalculatorTest {
    @Mock Adder adder;
    @Mock Subtractor subtractor;
    @Mock Divider divider;
    @Mock Multiplier multiplier;

    Calculator calculator;

    @Before
    public void setUp() {
        calculator = new Calculator(adder,subtractor,divider,multiplier);
    }

    @Test
    public void testAdd() {
        BigDecimal value = calculator.add(2,2);
        verify(adder).add(eq(2),eq(2));
    }
}
q9rjltbz

q9rjltbz3#

我更喜欢使用构造函数来初始化我的测试对象,因为它允许我将所有成员设置为final,以便IDE或编译器在 * 构造函数 * 忘记初始化成员时告诉我,并防止其他方法设置它们。
恕我直言,@Before违反了最重要的Java约定之一,即依赖构造函数来完全初始化对象!
JUnit 5对构造函数注入也有更好的支持。

vwhgwdsa

vwhgwdsa4#

我更喜欢将fixture声明为final,并内联或在构造函数中初始化它们,这样我就不会忘记初始化它们!然而,由于在@Before中抛出的异常是以一种更用户友好的方式处理的,所以我通常在@Before中初始化测试对象。

lawou6xi

lawou6xi5#

引用自http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/
您可能想知道为什么要编写setUp()方法,而不是简单地在测试用例的构造函数中初始化字段。毕竟,由于测试用例的每个测试方法都创建了一个新的示例,所以构造函数总是在setUp()之前调用。在绝大多数情况下,您可以使用构造函数而不是setUp(),而不会产生任何副作用。
在测试用例是更深层次继承层次结构的一部分的情况下,您可能希望推迟对象初始化,直到派生类的示例完全构造完成。这是一个很好的技术原因,说明为什么您可能希望使用setUp()而不是构造函数进行初始化。使用setUp()和tearDown()也有利于文档的目的,因为它可以使代码更容易阅读。

zengzsys

zengzsys6#

有一件事 constructor 可以存档,但 @Before 不能。
当你需要初始化父类中定义的字段时,你必须使用构造函数。举例来说:

abstract class AbstractIT {
   int fieldAssignedInSubClass;
   public AbstractIT(int fieldAssignedInSubClass) {
      this.fieldAssignedInSubClass= fieldAssignedInSubClass;
   }

   @Before
   void before() {
      // comsume fieldAssignedInSubClass
   } 
}

public class ChildIT extends AbstractIT{
   public ChildIT() {
      // assign fieldAssignedInSubClass by constructor
      super(5566); 
   }

   @Before
   void before() {
      // you cannot assign fieldAssignedInSubClass by a @Before method
   } 
}
3gtaxfhh

3gtaxfhh7#

@Before确实有意义,原因有几个。它使您的测试代码更具可读性。它匹配@After注解,@After注解负责释放已使用的资源,并且是@BeforeClass注解的对应物。

1szpjjfi

1szpjjfi8#

除了构造函数是ehere可以初始化@Rule对象的唯一方法之外,没有什么区别:

public class TestClass {

    @Rule
    public SomeRule rule;

    public TestClass() {
        // code to initialize the rule field
        conf = new RuleConf()
        rule = new SomeRule(conf)
    }
}

相关问题