24

在遵循我工作场所的“架构师”传下来的不良做法并认为必须有更好的方法之后,我最近一直在阅读有关 TDD 和 DDD 的内容,我认为这些原则和做法将是一个非常适合我们编写的软件的复杂性。

但是,我见过的许多 TDD 示例调用域对象上的方法,然后测试对象的属性以确保行为正确执行。

另一方面,业内几位受人尊敬的人(Greg Young 在他关于 CQRS 的演讲中最引人注目)主张通过删除所有“getter”来完全封装每个域对象。

因此,我的问题是:如果禁止检索其状态,如何测试域对象的功能?

我相信我缺少一些基本的东西,所以请随时称我为白痴并启发我 - 任何指导将不胜感激。

4

8 回答 8

17

您所描述的是状态验证,其中您断言域对象的状态。TDD 有一个分支,称为行为验证,它使用 Mock 对象。

行为验证允许您指定应该调用哪些方法,以及如果需要,哪些方法不被调用。

查看 Martin Fowler 的这篇文章了解更多详情:Mocks Aren't Stubs

于 2009-07-17T20:58:42.463 回答
9

好的,这个答案已经晚了一年;-)

但是当您想测试 CQRS 模型时,您可以对触发的域事件进行断言,而不是对实体状态进行断言。

例如,如果您想测试调用是否:customer.Rename("Foo") 会导致正确的行为。

与其测试 customer.Name 是否等于“foo”,不如测试在您的待处理事件存储中是否存在一个值为“Foo”的待处理 CustomerRename 事件。(在您的 uow 或实体事件列表中,具体取决于实施)

于 2010-05-05T19:05:54.390 回答
4

如果您真的要禁止检索状态,那么您将仅限于行为测试,可能通过诸如 TypeMock 之类的模拟框架,它具有跟踪对象行为的能力。如果您能够进行纯 BDD,那么理论上您可以仅通过其行为方式来断言整个系统的正确性。

在实践中,我发现 BDD 在很多情况下比有状态测试更脆弱。虽然有些人可能会要求某种理论,但它只适用于你。基于状态的测试仍然占我们编写的所有单元测试的 90%,而且我们非常了解我们团队中的 BDD。

做最适合你的事情。

于 2009-07-17T21:03:21.003 回答
2

几件事。

首先,当你做 TDD 之类的事情来使你的代码可测试时,你最终会得到更小的类。如果你的类有很多你无法检查的私有属性,那么它很有可能被分解成多个类并变得更可测试。

其次,老派的 OO 架构试图通过使用语言保护措施来防止软件被访问,从而使软件安全。TDD 架构通过编写测试来验证代码实际做了什么,从而使软件更加健壮,而不那么强调使用语言结构来确保程序不做什么。

最后,检查属性并不是验证代码是否完成了它应该做的唯一方法。xUnit Design Patterns 一书在这里记录了其他方法:http: //xunitpatterns.com/Result%20Verification%20Patterns.html

于 2009-07-17T20:58:42.447 回答
2

我调用系统的公共输入方法(即我将输入数据推送到系统中),然后我获取(并断言)系统的输出。我不是在测试系统的内部状态,而是在测试它的公共/可见行为:应该测试内部实现,还是只测试公共行为?

于 2009-07-17T21:04:57.910 回答
2

您提到的称为状态测试。还有行为测试。用于此的技术是依赖注入、控制反转和模拟:

你的类的所有副作用都被实现为对其“依赖项”的方法调用——即从外部提供的对象,通常在构造函数中。然后,在您的单元测试中,您提供一个假对象而不是真实对象。假对象可以记住是否调用了它的某个方法,这就是您在测试中断言的内容。

存在许多通过动态生成实现给定接口的类来自动创建模拟对象的模拟框架。最受欢迎的是 Rhino.Mocks 和 Moq。

于 2009-07-17T21:13:46.223 回答
2

嘿,贾斯汀,和你一样,我最近考虑在我的只写域对象中添加 getter 以进行单元测试,但现在我确信我错了。假设您一开始就接受了只写域的想法,那么如果您有吸气剂,那您就是在自找麻烦。只写域原则希望您从域对象触发事件,或从域对象编写的投影中读取,或类似的东西。一旦你暴露了吸气剂,你就开始暴露对象的“形状”,正如 Greg Young 所说,“域对象有行为,而不是形状”。

话虽如此,我正在为同样的问题而苦苦挣扎……您如何对只写域对象进行单元测试?这是我目前的计划:我正在考虑让我的域对象触发一个域事件,说“这些属性已更改”,并且在我的单元测试中,我将在发送“EditCommand”之前注册它。在此处查看 Udi Dahan 关于域事件的帖子,还可以查看Eric Evans 对域事件的看法。

于 2010-02-22T23:59:00.977 回答
1

在我最终偶然发现以下论文之前,我一直在想同样的事情。我发现它们是执行行为验证的重要入门,尤其是第一个为我提供了几个“啊哈时刻”:

  1. 使用模拟和测试来设计基于角色的对象
  2. 模拟角色,而不是对象
于 2010-04-27T19:58:31.937 回答