【问题标题】:Writing mocks/stubs for an object before you have written the class for that object?在为该对象编写类之前为该对象编写模拟/存根?
【发布时间】:2012-01-18 09:24:10
【问题描述】:

我正在设计一个具有两个依赖项的类。其中一个依赖类已被编写和测试。另一个还没有写完。

我突然想到,因为将编写剩余的依赖项以方便将使用它的类,我应该先编写后者,并在我进行的过程中设计前者的接口,学习它应该做什么。

在我看来,这是编写代码的好方法。毕竟,只要主类在其构造函数中得到一个模拟,我就可以编写它并测试它而不知道它的依赖项不存在,然后我可以在确定我知道我需要什么后创建依赖项.

那么:我该怎么做?创建一个我修改的骨架类。也许是这样的:

class NonExistantSkeleton
{
    public function requiredMethod1()
    {
    }

    public function newlyDiscoveredRequirement()
    {
    }
}

然后使用 PHPUnit 模拟它,并设置存根等,以使我的课程在开发过程中保持愉快?

这是要走的路吗?

这似乎是一种开发代码的好方法——在我看来,这比开发依赖项更有意义,而无需真正知道如何使用它。

【问题讨论】:

    标签: php phpunit class-design


    【解决方案1】:

    简而言之:

    是的。至少这就是我现在正在做的事情。


    加长版:

    如果您的班级的预期合作者在您正在构建的班级的测试中需要他们的时间点不存在,您有几个选项

    • 模拟不存在的类(phpunit 可以做到)
    • 创建类骨架并模拟它们
    • 只需创建接口并为这些接口获取模拟(phpunit 也可以)
    • 根据对象,您可能不需要上述任何一项

    如果您针对某个接口编程,那么您需要做的就是创建该接口并告诉 PHPUnit 从中创建一个存根/模拟

    • +没有测试就没有新课程
    • +在适当的时候使用接口被认为比仅仅提示类更好/更好

    模拟不存在的类时,您会遇到一些我不喜欢的缺点:

    • -模拟维护成本高
    • - 更改该类的方法既缓慢又乏味
    • -如果你创建了这个类,你应该再次修改模拟

    所以我建议不要这样做。

    中间方法是用它的方法创建一个空的类骨架,然后用它们来模拟。

    我非常喜欢在没有接口提示的情况下这种方式,因为它速度快并且可以创建稳定的测试代码。

    对我来说,拥有带有公共 api 的准系统类并不违反 TDD。


    有些类你不需要模拟。

    Data transfer objectsValue Objects 始终可以在您的生产代码中使用new 在任何地方创建,因此您的测试也可以只是真实对象

    这有助于使您的测试更简洁,因为您不需要模拟/期望大量的 getter/setter 方法等等。

    【讨论】:

    • 接口对于测试和替代性来说都是一个巨大的推动力,我应该在我的回答中提到它们,所以 +1
    • 谢谢,太好了。我认为也许花费额外的时间来设计类、它们的协作和 API,然后创建接口(可以模拟)似乎是一种更好的做事方式。你用什么软件来设计课程?有什么推荐的吗?
    • @LewisBassett 设计你的课程本质上是一种智力练习,而那里有 CASE/UML 工具可以帮助我说笔和纸作为软件设计工具从未被超越; )
    • @LewisBassett 我所有的设计都是在我的 IDE 中编写 PHP 代码(或事先在纸上绘图)。如果您喜欢 UML,您也可以这样做,但我希望我的体系结构可执行/可编译,而且我不知道有任何真正完整的 PHP 实体 UML 建模工具。 “EA”(企业架构师东西钱)是我见过的最好的,但有很多缺点。 ---
    【解决方案2】:

    如果您遵循 开发方法,那么通常的方法如下:

    1. 弄清楚您的类的用途,以及它们面向公众的 API 应该是什么。
    2. 实现“空”类,这些类只包含具有空主体的公共方法签名(正如您在给出的代码示例中所做的那样)。
    3. 制定实施策略。这意味着找出哪些类相互依赖,并按顺序实现它们,这意味着依赖类在它所依赖的类完成之前不会实现,或者至少具有足够的功能来开发。这意味着首先执行没有依赖关系的类,然后执行仅依赖于已完成类的类,依此类推。
    4. 编写测试。现在可以编写测试,因为您知道类的黑匣子是什么样子,它们需要作为输入以及它们应该作为输出返回什么。
    5. 运行测试。您应该获得 0% 的成功,但也应该获得 100% 的代码覆盖率。现在这是你的基线。
    6. 根据您的实施策略开始实施您的类。在这个过程中不时地运行你的单元测试,比如说一旦你完成了一个类,以确保它符合单元测试中规定的规范。理想情况下,每个测试都应该显示测试通过率的增加,同时保持 100% 的代码覆盖率。

    编辑:正如 edorian 所指出的,PHP 接口在这里有很大的帮助,因为 PHPUnit 可以从接口和类生成模拟和存根。一般来说,它们也是减少耦合和提高可替代性的绝佳工具。它们允许您替换任何实现预期接口的类,而不仅仅是预期类的子类。

    【讨论】:

    • 谢谢@Gordon。我同意您的流程 - 今天早上我已经为一个依赖于尚不存在的对象的类编写测试和代码,很明显这并不理想。我唯一不同意的是在编写代码之前编写所有测试。根据 Wikipedia(现在已经关闭!) - 测试驱动开发是关于编写一个测试,实现代码以使其通过,然后编写另一个测试,并实现代码以使其通过,等等。 - en.wikipedia.org/wiki/Test-driven_development
    猜你喜欢
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2012-06-04
    • 1970-01-01
    相关资源
    最近更新 更多