10

我有一个带有大量单元测试的参数化测试类,这些单元测试通常控制自定义电子邮件的创建。现在班级有很多测试取决于参数化班级中使用的因素,每个测试的测试流程都是相同的。一个测试的例子:

@Test
public void testRecipientsCount() {
    assertEquals(3, recipientsCount);
}

我必须在我的电子邮件类中添加额外的功能,将一些额外的内部电子邮件添加到收件人列表中,而这只发生在某些情况下,这会导致我的问题。

假设我想断言创建的消息数量。对于旧测试,每种情况都相同,但现在根据情况不同。对我来说最直观的方法是添加 if 语句:

@Test
public void testRecipientsCount(){
    if(something) {
        assertEquals(3, recipientsCount);
    }
    else {
        assertEquals(4, recipientsCount);
    }
}
...

但我更有经验的同事说我们应该避免在测试课程中使用 ifs(我有点同意这一点)。

我认为对两个测试类进行拆分测试可能会起作用,但这会导致两个类中的代码冗余(我仍然必须检查是否创建了非内部消息、它们的大小、内容等),并添加了几行其中之一。

我的问题是:我该怎么做才能不使用 if 或大量冗余代码(不使用参数化类会产生更多冗余代码)?

4

4 回答 4

4

在我看来,Junit 应该像协议一样阅读。这意味着您可以编写冗余代码以使测试用例更具可读性。为业务逻辑中每个可能的 if 语句以及否定用例编写一个测试用例。这是获得 100% 测试覆盖率的唯一方法。我使用的结构:

- testdata preparation
- executing logic
- check results
- clear data

此外,您应该在自己的抽象类中编写大对象的复杂断言:

abstract class YourBusinessObjectAssert{
  public static void assertYourBussinessObjectIsValid(YourBusinessObject pYourBusinessObject,       Collection<YourBusinessObject> pAllYourBusinessObject) {
for (YourBusinessObject lYourBusinessObject : pAllYourBusinessObject) {
  if (lYourBusinessObject.isTechnicalEqual(pYourBusinessObject)) {
    return;
  }
}
assertFalse("Could not find requested YourBusinessObject in List<YourBusinessObject>!", true);
}
}

它将降低代码的复杂性,并且您可以将其提供给其他开发人员。

于 2013-03-01T09:15:43.220 回答
1

在我看来,单元测试应该尽可能只测试一件事。因此,我会说,如果您需要一个 if 语句,那么您可能需要多个单元测试 - if/else 代码中的每个块都需要一个单元测试。

如果可能的话,我会说测试应该读起来像一个故事——我喜欢的布局(这不是我的想法:-)——它被广泛使用)是:

- given:  do setup etc
- when:  the place you actually execute/call the thing under test
- expect: verify the result

单元测试只测试一件事的另一个优点是,当失败发生时,它的原因是明确的——如果你有一个很长的测试,有很多可能的结果,那么推理测试失败的原因就变得更加困难。

于 2013-03-01T09:29:17.867 回答
1

我不确定是否有可能在参数化测试中干净利落地做你想做的事情。如果您需要基于某些功能的哪个参数的不同测试用例行为,您最好单独测试这些功能 - 在未参数化的不同测试类中。

如果您确实想将所有内容保留在参数化测试类中,我会倾向于制作一个辅助函数,以便您的示例测试至少读作一个简单的断言:

@Test
public void testRecipientsCount(){
    assertEquals(expectedCount(something), recipientsCount)
}

private int expectedCount(boolean which) {
    if (something){
       return 3;
    }
    else {
       return 4;
    }
}
于 2013-03-02T00:47:24.043 回答
0

为什么不使用私有方法来测试每种方法的共同点?类似于(但可能带有该方法的一些输入参数testCommonStuff()):

@Test
public void testRecipientsCountA(){
   testCommonStuff();
   // Assert stuff for test A
}

@Test
public void testRecipientsCountB(){
   testCommonStuff();
   // Assert stuff for test B
}

private void testCommonStuff() {
   // Assert common stuff
}

这样你就不会得到多余的代码,你可以把你的测试分成更小的测试。如果他们实际上应该测试相同的东西,那么你也可以让你的测试更不容易出错。您仍然会知道哪个测试失败了,因此可追溯性应该没有问题。

于 2013-03-01T09:55:05.323 回答