Ain*_*ugh 5 java testing unit-testing mocking mockito
我正在搜索如何测试某些内容是否已从数据库中正确删除,我找到了这个答案:/sf/answers/2665796241/但这让我思考,如果删除方法失败并且没有失败怎么办实际上删除了该对象,然后呢?verify 方法是否只检查删除方法是否被调用过一次,或者是否被调用过一次并且是否成功?因为如果它不检查删除是否成功,那么该测试根本没有用。
你的观点是相关的。
Mockito.verify()
将仅验证在方法执行期间是否在模拟上完成了调用。
这意味着如果模拟类的真正实现没有按预期工作,则测试是无助的。
因此,您在测试中模拟的类/方法也必须进行统一测试。
但这是否意味着verify()
一切都好呢?并不真地。
假设一个测试删除某些内容的方法:
public class Foo{
MyDao myDao;
public void delete(int id){
myDao.delete(id);
}
}
Run Code Online (Sandbox Code Playgroud)
测试通过:
@Mock
MyDao myDaoMock;
Foo foo;
@Test
public void delete(int id){
foo.delete(id);
Mockito.verify(myDaoMock).delete(id);
}
Run Code Online (Sandbox Code Playgroud)
假设现在我将实现更改为:
public void delete(int id){
myDao.delete(id);
myDao.create(id);
}
Run Code Online (Sandbox Code Playgroud)
测试仍然是绿色的......哎呀。
其他场景,假设一个主要调用依赖方法的方法:
public void doThat(){
Foo foo = fooDep.doThat(...);
Bar bar = barDep.doThat(foo);
FooBar fooBar = fooBarDep.doThat(foo, bar);
fooBis.doOtherThing(...);
// and so for
}
Run Code Online (Sandbox Code Playgroud)
通过验证方法,单元测试将仅以 Mockito 格式描述/复制方法的实现。
它对返回结果没有任何断言。以错误的方式更改实现(添加不正确的调用或删除所需的调用)很难通过测试失败来检测,就像测试只是调用语句的反映一样。
模拟验证通常需要谨慎使用。
在某些特定情况下,它可能会有所帮助,但在许多情况下,我看到开发人员滥用它(75%或更多的单元测试类是模拟设置/记录/验证),因此它产生了一个膨胀的单元测试价值不大,很难维护,而且还会因为不公平的原因减慢你的构建速度。
事实上,对于基本上依赖于具有副作用的函数的测试方法,集成测试(甚至切片/部分)应该受到青睐。
Mocks Aren’t Stubs of Martin Fowler是一篇优秀的文章,您应该会感兴趣。
当我观察一个模拟程序员时,这一点给我留下了特别深刻的印象。我真的很喜欢这样一个事实:在编写测试时,您关注的是行为的结果,而不是行为的完成方式。模拟者不断思考 SUT 将如何实现,以便写出期望。这对我来说真的很不自然。
虽然 Martin Fowler 的这篇文章很有趣,但我也不同意所有观点。
我同意采用模拟方法,开发人员不会模拟依赖项,因为依赖项很烦人,但系统地这样做通常是一个坏主意。
我们应该总是有充分的理由来引入模拟,例如:
但我不同意显式创建存根通常是一个好主意,因为存根代码需要时间编写,可能有错误,我们必须维护它。最后,为了使事情变得干净健壮,存根类也应该进行统一测试。所有这一切都是有代价的。
另一方面,模拟库生成的模拟没有所有这些缺陷。
没有什么可以阻止我们以存根的方式使用这些模拟:使模拟作为存根进行协作,即:
verify()
verify()
滥用