何时使用Mockito.verify()?

我为三个目的编写jUnittesting用例:

  1. 为了确保我的代码在全部(或大部分)input组合/值下满足所有必需的function。
  2. 为了确保我可以更改实现,并依靠JUnittesting用例告诉我,我所有的function仍然满足。
  3. 作为我的代码处理的所有用例的文档,并作为重构的规范 – 如果代码需要重写。 (重构代码,如果我的jUnittesting失败 – 你可能错过了一些用例)。

我不明白为什么或什么时候应该使用Mockito.verify() 。 当我看到verify()被调用时,它告诉我,我的jUnit正在意识到实现。 (因此改变我的实现将打破我的jUnits,即使我的function不受影响)。

我在找:

  1. 什么应该适当使用Mockito.verify()的指导方针?

  2. jUnits是否意识到或紧密结合被testing的类的实现从根本上说是正确的?

这是一个奇妙的问题。 +1。

如果A类的契约包含它调用typesC的对象的方法B的事实,则应该通过对Ctypes进行模拟并validation方法B已被调用来进行testing。

这意味着类A的契约具有足够的细节,它涉及typesC(可能是接口或类)。 所以是的,我们正在谈论的规范水平不仅仅是“系统要求”,而且还有一些描述实现的方式。

这对于unit testing是正常的。 当你在进行unit testing时,你要确保每个单元都在做“正确的事情”,这通常包括与其他单元的交互。 这里的“单位”可能意味着您的应用程序的类或更大的子集。

更新:

我觉得这不仅仅适用于validation,也适用于存根。 一旦你存在一个协作者类的方法,你的unit testing在某种意义上就变成了实现。 unit testing的本质就是这样。 由于Mockito和validation一样多,所以你使用Mockito的事实意味着你将会碰到这种依赖关系。

根据我的经验,如果我改变一个类的实现,我经常不得不改变它的unit testing的实现来匹配。 但是,通常情况下,我不需要更改class级的unit testing清单。 当然,除非有更改的原因,否则我之前没有做过testing。

所以这就是unit testing的内容。 不使用合作者类的这种依赖性的testing实际上是子系统testing或集成testing。 当然,这些也是经常用JUnit编写的,经常会涉及到使用嘲笑。 在我看来,“JUnit”是一个很糟糕的名字,因为这个产品可以让我们产生所有不同types的testing。

大卫的答案当然是正确的,但不能解释你为什么要这样做。

基本上,当unit testing你正在孤立地testing一个function单元。 您testinginput是否产生预期的输出。 有时候,你也必须testing副作用。 简而言之,validation允许你这样做。

例如,你有一些业务逻辑,应该存储使用DAO的东西。 你可以使用集成testing来实现这个DAO,把它连接到业务逻辑,然后在数据库中查找是否存储了预期的东西。 这不是一个unit testing。

或者,您可以嘲笑DAO并validation它是否按照您期望的方式被调用。 使用mockito,你可以validation被调用的东西,被调用的频率,甚至在参数上使用匹配器,以确保它以特定的方式被调用。

像这样的unit testing的另一面确实是你正在将testing绑定到实现,使得重构有点困难。 另一方面,良好的devise气味是正确使用它的代码量。 如果你的testing需要很长的时间,那么devise可能是有问题的。 所以需要testing的代码有很多副作用/复杂的交互可能不是一件好事。

这是个很好的问题! 我认为它的根本原因是以下,我们正在使用JUnit不仅用于unit testing。 所以这个问题应该分解:

  • 我应该在我的集成 (或任何其他高于unit testing)testing中使用Mockito.verify()吗?
  • 我应该在我的黑盒unit testing使用Mockito.verify()吗?
  • 我应该在我的白盒unit testing使用Mockito.verify()吗?

所以如果我们忽略高于单位的testing,这个问题就可以改写为“ 在Mockito.verify() 使用白盒unit testing在unit testing和我的可执行性之间产生了很好的联系,我可以创build一些”灰盒子“unit testing和我应该使用什么经验法则 ”。

现在,让我们一步一步的完成这一切。

* – 我应该在我的集成 (或任何其他高于unit testing)testing中使用Mockito.verify()吗?*我认为答案显然是否定的,此外,您不应该使用模拟。 您的testing应尽可能接近实际应用。 您正在testing完整的用例,而不是孤立的应用程序的一部分。

* 黑盒 vs 白盒unit testing*如果你使用黑盒子方法,你真的在​​做什么,你提供(所有等价类)input,一个状态 ,并testing,你会收到预期的输出。 在这种方法中,一般使用mock是正当的(你只是模仿他们做的是正确的事情,你不想testing它们),但是调用Mockito.verify()是多余的。

如果你使用白盒方法,你真的在​​做什么,你正在testing你的单位的行为 。 在这种方法中调用Mockito.verify()是必不可less的,你应该validation你的单位是否像你期待的那样。

灰盒testing的经验法则白盒testing的问题在于它创build了一个高度耦合。 一个可能的解决scheme是做灰盒testing,而不是白盒testing。 这是黑白盒testing的结合。 你真的在testing你的单元的行为 ,就像在白盒testing中一样,但是一般情况下, 如果可能的话,你可以使它与实现无关。 在可能的情况下,您只需进行黑箱的检查,只是声称输出是您期望的。 所以,你的问题的本质是什么时候有可能。

这真的很难。 我没有一个好例子,但我可以给你举例。 在上面提到的equals()vs equalsIgnoreCase()的情况下,你不应该调用Mockito.verify(),只是声明输出。 如果你做不到,把你的代码分解到更小的单元,直到你能做到。 另一方面,假设你有一些@Service,并且你写@ Web服务本质上是包装在你的@Service上 – 它把所有的调用委托给@Service(并且做一些额外的error handling)。 在这种情况下调用Mockito.verify()是必不可less的,您不应该复制您为@Serive所做的所有检查,validation您是否正在使用@Service调用正确的参数列表就足够了。

我必须说,从古典方法的angular度来看,你是绝对正确的:

  • 如果您首先创build(或更改)应用程序的业务逻辑 ,然后采用(采用)testingTest-Last方法 )来覆盖它 ,那么让testing知道有关软件的工作方式是非常痛苦和危险的,检查input和输出。
  • 如果您正在实践一种testing驱动的方法 ,那么您的testing是第一个被写入,将被改变并反映软件function的用例 。 实施取决于testing。 这有时候意味着你希望你的软件以某种特定的方式被实现,例如依靠其他组件的方法,甚至称它为特定的次数。 这是Mockito.verify()派上用场的地方!

重要的是要记住,没有通用的工具。 软件types,规模,公司目标和市场情况,团队技能以及其他许多因素都会影响您决定在特定情况下使用哪种方法。

正如有些人所说的

  1. 有时你没有一个你可以断言的直接输出
  2. 有时你只需要确认你的testing方法正在向合作者发送正确的间接输出(你正在嘲笑)。

关于重构时打破testing的问题,使用mocks / stub / spies时有点期待。 我的意思是根据定义,而不是像Mockito这样的具体实现。 但是你可以这样思考 – 如果你需要做一个重构,在你的方法工作的方式上会产生重大的改变,那么在TDD方法上做一个好主意,这意味着你可以先改变你的testing来定义新的行为(这将会使testing失败), 然后进行更改并再次通过testing。