【发布时间】:2014-10-11 12:03:57
【问题描述】:
我目前正在尝试做的是在单元测试中测试类的构造函数。
我不确定这个对象的实例是否是“上帝对象”,我会说不是,因为它只聚合了其他几个组件。
无论哪种方式,我都愿意接受更好的设计。
所以粗略的类图是这样的
World是疑似神级。它的依赖项ServiceProvider、CommandRegistry、EventHub 和Environment 通过其构造函数注入。
在其构造函数中,World 做了以下事情:
- 将其依赖项存储在私有字段中
- 向
eventHub注册一个钩子($this, 'onCommandIssued'),以便world接收有关不通过world实例本身执行的所有命令的通知(world还有一个方法executeCommand) - 告诉环境采用这个世界:
$this->environment->adoptWorld($this)。环境的作用是使世界适应运行环境的某些现实,例如,Web 环境具有一些在控制台应用程序环境中不可用的特定服务(例如“会话”服务) - 通过事件中心通知世界构建完成:
$this->eventHub->notify(new WorldConstructedEvent($this));
也许这看起来像一个沉重的构造函数,但它只是被定义为“构造世界”。
World 基本上是发送命令的网关(作为数据传输对象,通过World::executeCommand()),不同的服务可以向其注册钩子。
现在是问题/疑问:
- 我正在尝试对这个构造函数进行单元测试,但是我必须添加一堆
@uses注释,这使它感觉像是除了单元测试之外的其他任何东西。那么它是什么,功能测试?单元测试World很尴尬,测试其他任何东西真的很琐碎,而且我在任何其他测试中都没有看到这个问题,这让我问自己为什么会这样以及如何改进设计。 -
World是神物吗?它所做的只是聚合其他组件并将调用转发给它们。 - 如何正确地对
World的方法进行单元测试?如果我使用大量存根并依赖注入它们,它仍然是单元测试吗?
这是针对域驱动设计的(复杂)应用程序,我愿意接受可以使设计更好(可测试和解耦)的建议。
如果您需要更多详细信息,请在 cmets 中告诉我。
由于我不知道这次讨论会引向何方,我可能会改进我的问题。
【问题讨论】:
-
我认为您将提供一个模拟 EventHub 并检查是否调用了钩子方法。根据需要对其他依赖项重复此操作。我怀疑我在你的问题中遗漏了一些东西。
-
你的意思是一个存根
EventHub,对吧?测试的类是World,所以这将是我的唯一模拟。 -
无论如何谢谢,这是个好主意。
-
模拟和存根不一样:martinfowler.com/articles/mocksArentStubs.html。为了测试您的构造函数,我们关心的是使用预期的参数调用 EventHub 和 Environment 方法。作为一个 Mockest Tester(使用 Fowler 的术语),我会模拟依赖项。你可能更像是一个经典的测试人员。
-
AFAIK 模拟是测试断言的对象,而存根是您在 SUT 中注入的“模拟”,SUT 依赖于它(“依赖注入”)。
标签: php unit-testing testing domain-driven-design functional-testing