模拟对象和设置值之间的区别是什么

时间:2015-03-02 09:00:05

标签: php unit-testing

我知道我问这个问题意味着我可能完全不理解嘲笑。我知道为什么使用模拟(隔离),我知道如何使用模拟(每个FW和它的方式) - 但我不明白 - 如果模拟的目的是返回预期的值来隔离我的单元测试,我为什么要模拟一个对象,而不仅仅是自己创造价值?

为什么:

    $searchEngine = Mockery::mock('elasticSearch');
    $searchEngine->shouldReceive('setupDataConnection')->once()->andReturn("data connected");

    $insertData = new DataInserter;
    $this->assertEquals("data connected",$insertData->insertData($searchEngine));

而不是这个:

    $knownResult = "data connected";
    $insertData = new DataInserter;
    $this->assertEquals($insertData->insertData($searchEngine) ,$knownResult);

修改 抱歉错误,但在第二个例子中意外地没有包含insertData。

2 个答案:

答案 0 :(得分:2)

通过使用模拟,您可以访问其他信息,从一个直接像C风格结构(仅执行数据存储)的对象,然后几乎没有区别(除了关于调用您的模拟调用的断言通常相当有用,因为你可以确保你的值例如设置3次,而不是7(可能有一些可能导致潜在问题的中间值。)

模拟对于测试对象之间的交互非常有用。

现在,如果您的类执行更复杂的操作(例如访问数据库之类的资源,或者从文件读取/写入数据),那么模拟将变得挽救生命,因为它们几乎可以让您抽象出类的内部行为。没有测试(例如,假设您的类的第一个版本只是将值存储在内存中,下一个版本将它们存储在数据库中的特定位置,这样您就可以首先节省数据库的资源命中访问,其次能够有效地证明你的被测试的类正常工作,而不是证明你的数据访问类是有效的。如果你有一个失败的测试,那么你可以立即磨练这个问题,而不是试图去做如果问题出在您的数据提供者或数据访问者中,请输出。)

由于测试可以并行运行,因此某些资源可能会导致错误的故障。 在没有模拟的情况下极难测试的几个相互作用的例子是:

  1. 与数据库访问层对话的代码(确保查询正确,在连接上调用close(),并发送适当的SQL,而且您不会修改真实数据库。)
  2. 文件/套接字IO(再次确保数据一致)
  3. 系统调用(例如调用php exec)
  4. 任何依赖于线程/时间安排的东西(在PHP中没有那么多问题)
  5. GUI代码(再次,在PHP中几乎闻所未闻)但是如果我将语言转换为让我们说java更容易调用: 当(myDialogBoxMock).showOptionInputDialog()thenReturn。(" mockInput&#34) 然后尝试用子类或临时实现/子类伪造它。
  6. 调用必须仅使用特定值调用的特定方法。 验证(myMock.setValue(7),times(2)); verifyNoOtherInteractions(myMock);
  7. 这很大一部分也是速度,如果你在磁盘上读取一个文件,在一个庞大的代码库中可以说300/400次,那么你肯定会注意到使用模拟速度提高了。

    如果你有一个像PHP的EMMA / jacoco工具,你将能够有一个有效的代码覆盖率报告,以显示你的测试没有涵盖代码。在任何非平凡的应用程序中,您会发现自己试图弄清楚如何将测试对象置于特定状态以测试特定行为,使用DI的Mocks实际上是执行这些任务的工具。

答案 1 :(得分:0)

在第一个示例中,您为DataInserter提供了一个伪合作者($ searchEngine)。在assert语句中,您正在调用insertData,它将执行DataInserter的逻辑,该逻辑将与假$ searchEngine进行术语交互。通过提供伪造,您可以验证DataInserter的逻辑是否可以单独正常工作。

在第二个示例中,您没有测试DataInserter逻辑。您只是检查常量字符串$ knownResult是否等于“数据连接”,即您正在测试php的字符串相等性。据我所知,你正在构建一个新的DataInserter对象,但你甚至没有在测试中运用它的代码。