【问题标题】:phpunit and mock for many depended objects许多依赖对象的phpunit和mock
【发布时间】:2016-04-24 13:17:06
【问题描述】:

我只是想了解在以下情况下编写单元测试的“正确方法”是什么:

class Time
{
    public function __construct(Hour $h, Minute $m, Second $s) {}
}

class TimeRange
{
    public function __construct(Time $start, Time, $end)
    {
        if ($this->startTime > $this->endTime) {
            throw new \Exception;
        }
    }
}

目的是为 TimeRange 构造方法实现单元测试,这将有助于确保在某个时候抛出异常

在这种情况下如何正确获取时间对象的模拟(不获取模拟意大利面条)?

【问题讨论】:

  • 看起来你的 Time 类只是一个值对象,很可能它是不可变的。不要模拟值对象,只需创建它们。
  • @JakubZalas 是的,你是对的,这就是我之前所做的,但现在我遇到了问题,如果值对象的逻辑要改变,那么你所有的测试都会被破坏。认为维护这样的代码非常复杂。使用 Mock/Stub 对象可能会更容易
  • 值对象没有复杂的逻辑。它们是价值观。
  • 一些不变量可能已经够复杂了,但是你说得对,让它们尽可能简单是有意义的。好吧,想象一下 TimeRange 应该代表一天中从凌晨 2 点到第二天凌晨 2 点的时间段——这基本上代表了工作日。那么显示不变量就不会那么简单了。

标签: mocking phpunit fixtures stub


【解决方案1】:

模拟创建可能变得相当复杂。在生产代码中,复杂的任务由专门的类或模块处理。在这种情况下是一样的。您可以并且应该抽象类中的存根和模拟创建。这些类应该是测试套件的一部分。

以你的情况为例。你可以有一个类,它将返回一个Time 对象,该对象用特定的小时、分钟、秒进行初始化:

<?php

class FixtureFactory
{
    public function getNewTimeMock($hour, $minute, $second)
    {
        // code for creating the mock
    }
}

这里是相关部分的 XUnit 设计模式书的链接。有很多模式只是用于创建模拟:

http://xunitpatterns.com/Fixture%20Setup%20Patterns.html

【讨论】:

  • 感谢您的链接,您如何看待使用模拟与真实对象,就像上面建议的 Jakub Zalas 一样?
  • 我同意。尽可能使用实物。如果没有必要,不要让事情变得更复杂。但是,当必须使用模拟或存根时,模拟创建并非微不足道,使用一些帮助类创建它们很有用,这样测试代码就不会变得臃肿并且没有重复。
  • 但是如果我使用真实的对象,这意味着它是一个集成测试而不是单元,对吧?
  • 如果真实对象不访问数据库、网络或任何系统资源,我不会因为没有模拟所有依赖项而将测试视为集成测试。例如,您正在创建具有受控时间的 Time 对象。如果您使用系统时间来创建它,那么它可能不是单元测试,因为它可能有时会失败,但有时不会。
  • @user1016265 我只是说不要模拟值对象。嘲笑几乎所有其他合作者。
猜你喜欢
  • 1970-01-01
  • 2015-02-17
  • 1970-01-01
  • 2017-04-02
  • 2016-05-14
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 2020-12-09
相关资源
最近更新 更多