Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Mockito验证方法是如何工作的?_Java_Unit Testing_Testing_Mocking_Mockito - Fatal编程技术网

Java Mockito验证方法是如何工作的?

Java Mockito验证方法是如何工作的?,java,unit-testing,testing,mocking,mockito,Java,Unit Testing,Testing,Mocking,Mockito,我在研究如何测试某个东西是否从数据库中被正确删除,我发现了这个答案:但这让我思考,如果delete方法失败,并且它实际上没有删除对象,那该怎么办? verify方法是否只检查delete方法是否调用过一次,或者是否调用过一次并且成功? 因为如果它没有检查删除是否成功,那么该测试就毫无用处。你的观点是相关的。 Mockito.verify()将只验证在方法执行期间是否对模拟进行了调用。 这意味着,如果模拟类的实际实现不能正常工作,测试将无能为力。 因此,您在测试中模拟的类/方法也必须进行单一测试。

我在研究如何测试某个东西是否从数据库中被正确删除,我发现了这个答案:但这让我思考,如果delete方法失败,并且它实际上没有删除对象,那该怎么办? verify方法是否只检查delete方法是否调用过一次,或者是否调用过一次并且成功?
因为如果它没有检查删除是否成功,那么该测试就毫无用处。

你的观点是相关的。
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%或更多的单元测试类是模拟设置/记录/验证),因此它产生了一个膨胀的单元测试,几乎没有价值,很难维护,并且由于不公平的原因也会减慢构建速度。
事实上,对于基本上依赖于具有副作用的函数的测试方法,应该支持集成测试(甚至是切片/部分测试)


这是一篇你应该感兴趣的好文章

当我观察到一个嘲弄者时,我尤其感到震惊 程序员我真的很喜欢这样的事实,在写测试的时候 关注行为的结果,而不是它是如何完成的。嘲弄者是 不断思考SUT将如何在 为了写下期望。我觉得这很不自然

虽然这篇马丁·福勒的文章很有趣,但我也不完全同意。
我同意mockist方法,即开发人员不模拟依赖项,因为依赖项令人讨厌,但系统地模拟依赖项,这通常是一个坏主意
我们应该始终有充分的理由引入模拟,例如:

  • 外部系统依赖性
  • 调用缓慢
  • 再现性问题
  • 与依赖项(输入和/或输出)交互的复杂设置
但我不同意显式创建存根通常是一个好主意,因为存根代码需要时间编写,可能有bug,我们必须维护它。最后,为了使事情干净、健壮,还应该对stubs类进行统一测试。所有这些都有代价。
另一方面,由模拟库生成的模拟没有所有这些缺陷。
没有什么能阻止我们以存根思维使用这些模拟:让模拟作为存根进行协作,即:

  • 输入/输出记录为是
  • 对于少数相关的
    verify()
  • 但不适用于
    verify()
    滥用
  • 对于将测试与实现耦合的细粒度模拟行为记录的乘法,则没有

您所指的示例来自:

而且,您是对的,当在单元测试期间模拟
delete
方法时,您只会判断是否调用了
delete
,而不会判断删除是否成功。
delete
失败的两种可能性是什么

a)
delete
调用不正确,可能缺少某个参数,或者需要为删除做准备。这样的问题在单元测试中是找不到的:如果您关于如何调用另一个组件的假设是错误的,并且您模拟了另一个组件,那么您实现模拟的方式只会反映您自己的误解,单元测试就会成功。然而,这并不是单元测试的一个缺陷:这就是为什么除了单元测试之外,还存在其他级别(如集成测试)的原因,集成测试旨在发现单元测试无法发现的缺陷。集成测试实际上是为了在组件之间的交互中发现bug

b)
delete
有时可能在运行时失败,无论您是否正确调用了
delete
方法。例如,您的代码可能没有对
personRepository
的写访问权限,或者某个并行线程同时删除了此人或其他内容。但是,示例代码没有任何措施来处理这样的运行时场景(好吧,它只是一段示例代码,但也可能是故意这样,请参阅davidxxx的注释)。但是,我们假设应该有一些代码来处理未成功的
delete

如果正确地进行模拟(即通过查看
delete
的规范),那么在单元测试期间,
delete
可能会失败。在这种情况下,可能会返回错误代码或引发异常。开发人员在意识到这一点时,可以决定通过相应的错误处理代码扩展上述示例代码。而且,可以通过模拟
delete
对错误处理代码进行单元测试,这样这些错误场景也可以
public void deleteFromPerson(person person) {
    person = personRepository.returnPerson(person.getId());
    personRepository.delete(person);
}