我一直想知道单元测试中 stub 的一般用途与使用真实(生产)实现的情况,特别是在使用 stub 时我们是否不会遇到相当讨厌的问题,如下所示:
假设我们有这个(伪)代码:
public class A {
public int getInt() {
if (..) {
return 2;
}
else {
throw new AException();
}
}
}
public class B {
public void doSomething() {
A a = new A();
try {
a.getInt();
}
catch(AException e) {
throw new BException(e);
}
}
}
public class UnitTestB {
@Test
public void throwsBExceptionWhenFailsToReadInt() {
// Stub A to throw AException() when getInt is called
// verify that we get a BException on doSomething()
}
}
现在假设我们在稍后的某个时刻,当我们编写了数百个测试时,意识到 A 不应该真正抛出 AException,而应该抛出 AotherException。我们纠正这个问题:
public class A {
public int getInt() {
if (..) {
return 2;
}
else {
throw new AOtherException();
}
}
}
我们现在已经更改了 A 的实现以抛出 AotherException,然后运行所有测试。他们通过了。不太好的情况是 B 的单元测试通过了,但是是错误的。如果我们在这个阶段将 A 和 B 放在生产中,B 将传播 AotherException,因为它的实现认为 A 抛出 AException。
如果我们在 throwsBExceptionWhenFailsToReadInt 测试中使用 A 的实际实现,那么在 A 更改后它就会失败,因为 B 不会再抛出 BException。
这只是一个可怕的想法,如果我们有数千个像上面的例子一样构造的测试,并且我们改变了一件小事,那么所有的单元测试仍然会运行,即使许多单元的行为是错误的!我可能遗漏了一些东西,我希望你们中的一些聪明人能够启发我它是什么。
最佳答案
当你说
We have now changed the implementation of A to throw AOtherException and we then run all our tests. They pass.
我认为这是不正确的。显然你还没有实现你的单元测试,但是B类不会捕获AException,因此不会抛出BException,因为AException现在是AotherException。也许我遗漏了一些东西,但是您的单元测试不会在断言此时抛出 BException 时失败吗?您将需要更新类代码以正确处理 AotherException 的异常类型。
关于unit-testing - 测试: stub vs real implementation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9708730/