您的观点是相关的。
Mockito.verify() 只会验证在方法执行期间是否在模拟上完成了调用。
这意味着如果模拟类的真正实现不能正常工作,测试是无能为力的。
因此,您在测试中模拟的类/方法也必须进行单一测试。
但这是否意味着verify() 总是好的?并不真地。
假设一个测试方法删除了一些东西:
public class Foo{
MyDao myDao;
public void delete(int id){
myDao.delete(id);
}
}
测试通过:
@Mock
MyDao myDaoMock;
Foo foo;
@Test
public void delete(int id){
foo.delete(id);
Mockito.verify(myDaoMock).delete(id);
}
假设现在我将实现更改为:
public void delete(int id){
myDao.delete(id);
myDao.create(id);
}
测试仍然是绿色的...哎呀。
其他场景,假设一个主要调用依赖方法的方法:
public void doThat(){
Foo foo = fooDep.doThat(...);
Bar bar = barDep.doThat(foo);
FooBar fooBar = fooBarDep.doThat(foo, bar);
fooBis.doOtherThing(...);
// and so for
}
使用验证方法,单元测试只会以 Mockito 格式描述/复制您方法的实现。
它对返回的结果没有任何断言。以不好的方式更改实现(添加不正确的调用或删除所需的调用)很难检测到测试失败,就像测试只是被调用语句的反映。
模拟验证通常需要谨慎使用。
在某些特定情况下,它可能会有所帮助,但在许多情况下,我看到开发人员滥用它(75% 或更多的单元测试类是模拟设置/记录/验证),因此它产生了一个膨胀的单元测试价值很少,很难维护,而且由于不公平的原因也会减慢您的构建速度。
事实上,对于本质上依赖于具有副作用的函数的测试方法,应该优先考虑集成测试(甚至切片/部分)。
Mocks Aren't Stubs of Martin Fowler 是一篇很棒的帖子,你应该会感兴趣。
当我观察到一个模仿者时,这让我特别震惊
程序员。我真的很喜欢这样一个事实,即在编写测试时你
关注行为的结果,而不是如何完成。一个嘲笑者是
不断思考 SUT 将如何在
为了写期望。这对我来说真的很不自然。
虽然 Martin Fowler 的这篇文章很有趣,但我也不完全同意。
我同意开发人员不模拟依赖项的模拟方法,因为依赖项很烦人,但系统地这样做是通常是个坏主意。
我们应该总是有充分的理由引入一个模拟,例如:
- 外部系统依赖
- 调用缓慢
- 重现性问题
- 与依赖项(输入和/或输出)交互的复杂设置
但我不同意创建显式存根通常是一个好主意,因为存根代码需要时间来编写,可能有错误,我们必须维护它。最后,为了使事情变得干净健壮,存根类也应该进行单一测试。所有这些都是有代价的。
另一方面,由 mocking 库生成的 mock 没有所有这些缺陷。
没有什么能阻止我们带着存根使用这些模拟:让模拟作为存根进行协作,即:
- 是的,用于输入/输出记录
- 是的,少数和相关
verify()
- 但
verify() 滥用则不行
- 不用于将测试与实施相结合的细粒度模拟行为记录的乘法