破坏单元测试的语义

jol*_*ier 3 java junit unit-testing

我正在为我们的主要产品编写单元测试,并且有一个问题:如何区分

  • 测试失败,因为他们测试的是错误的(发现了一个错误,例如它的非回归测试)
  • 测试失败,因为测试的另一个意外部分失败(因为测试错误或出现未知错误)

对于第一个,我们肯定有jUnit Assert框架,但是对于第二个框架我们有什么?

示例:我的单元测试是测试c()不会抛出MyException,但是为了执行c()我需要首先执行a()然后b(),它们都可以抛出MyException(),所以我会写:

@Test
public void testC() {
  a();
  Object forC = b();
  try {
    c(forC);
  } catch (MyException e) {
    Assert.fail("....");
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我需要处理可以被a或b抛出的MyException,并且还要处理forC不应该为null的事实.做这个的最好方式是什么?

  • 捕获由a或b和Assert.fail抛出的MyException,但是a和b未经过此测试的测试,因此对我来说,它们在失败时不应被标记为测试失败.也许他们以后失败了,因为此时我们应该做b(); a()而不是a(); b();.
  • 让testC抛出MyException,以便测试将以"MyException"失败,但这是误导性的,因为MyException不会告诉测试写错了.然后所有测试都将失败,每个测试都有自己的异常.在这种情况下,如果forC为null,我也需要抛出类似NullPointerException的东西,它也没有语义.
  • 捕获并将由a和b抛出的MyException包装成异常,告知测试可能是错误的,如TestCorruptedException.但我无法在jUnit中找到这样的异常,因此jUnit不会识别它们(这对我来说没问题).此外,我需要从我的所有单元测试中了解这个异常,当然这些单元测试分为多个模块,项目等等......所以这是可行的但是增加了依赖性.

你对这个问题的解决方案是什么?我可能会选择第二个,但我对它不满意,如上所述.

Bra*_*rad 5

单元测试的基石是测试所需的最少代码量,否则它可能会落入您正在寻找端到端功能的集成测试空间.

如果你能证明a()可以创建并在它自己的测试类测试,而且b()还可以创建并在它自己的测试类测试,那么它遵循你的测试上面可以忽略的测试a()b(),并改用赢得已知值不要失败.这通常通过使用模拟对象来满足.

使用()和b()创建为模拟对象,c()可以单独测试.如果您发现无法单独测试c()和()和b(),则表明您的代码需要更改,以便关注点分离.这通常通过 a()和b()的dependany注入c()来满足.

关于何时在单元测试中使用模拟对象的这篇文章可能有助于更多地阐述这一主题.