Unit testing TDD和x2B;DDD:模型抽象

Unit testing TDD和x2B;DDD:模型抽象,unit-testing,domain-driven-design,tdd,Unit Testing,Domain Driven Design,Tdd,我最近有一个有趣的经历,但到目前为止还没有找到一个令人满意的答案:我是DDD的忠实粉丝,并试图用行为和良好的信息隐藏来定义丰富的域对象,即使团队官方没有实践DDD。归根结底,这并不重要,因为您有一个定义良好的对象,它表示问题域中的某些内容 也就是说,我还想多练习TDD。不幸的是,如果我测试一个使用如此丰富的域模型的服务,这些模型通常不会被抽象。因此,为了测试服务的行为,我还需要建立模型。这个模型有它自己的不变量等,因此在每次服务测试中,我也测试服务使用的模型 这似乎是一个很大的障碍,因为我不仅“

我最近有一个有趣的经历,但到目前为止还没有找到一个令人满意的答案:我是DDD的忠实粉丝,并试图用行为和良好的信息隐藏来定义丰富的域对象,即使团队官方没有实践DDD。归根结底,这并不重要,因为您有一个定义良好的对象,它表示问题域中的某些内容

也就是说,我还想多练习TDD。不幸的是,如果我测试一个使用如此丰富的域模型的服务,这些模型通常不会被抽象。因此,为了测试服务的行为,我还需要建立模型。这个模型有它自己的不变量等,因此在每次服务测试中,我也测试服务使用的模型

这似乎是一个很大的障碍,因为我不仅“不是真正的单元测试”,而且设置测试也很麻烦,因为代码越来越大。 在我看来,除了开始为模型创建接口之外,似乎没有别的办法。但似乎只有我这么想。例如,这里有一篇大文章,为什么这是一个反模式:


我也不太乐意为所有模型创建接口,因为它们应该真正代表一些东西,并且仅仅为了测试而添加另一层抽象似乎有些过分。也就是说,最好的解决方案是什么?将DDD和TDD结合在一起的现场人员是如何处理这一问题的?

基础知识

测试范围

  • 单元测试
  • 集成测试
域模型

  • 这些应该是单元测试,它测试域模型/聚合的方法
服务

  • 这些应该是集成测试,测试服务方法和相关模型的集成

我的广泛方法

当您测试域模型时,可能存在许多差异,您需要在单元测试中考虑这些差异

当这些转化为在集成测试中使用的需求时,我倾向于为您的域模型选择某种CreationFactory(或ArrangementFactory)

然后,您可以在这两组测试中使用它们

比如说

public类ArrangeUser{
公共静态用户ArrangeStandardUser(){
返回新用户(…标准…);
}
公共静态用户ArrangeAdminUser(){
返回新用户(…管理员…);
}
}
然后在单元测试中

//排列
用户标准用户=ArrangeUser.standardUser();
//表演
bool canDoSomething=standardUser.canDoSomething();
//断言
Assert.True(canDoSomething);
然后在集成测试中

//排列
用户标准用户=ArrangeUser.standardUser();
ServiceToTest服务=新ServiceToTest(标准用户);//替换为某种类型的存储库模拟或任何适合的东西。
//表演
var bool canDo=service.CanDoService();
//断言
Assert.True(canDo);
通过这种方式,您可以测试单元方面和服务方面——通过创建一种创建安排的通用方法,而不必抽象出实体,并反复解决同样的问题

注意。这只是一个基本的代码演示,可以根据场景或您喜欢的测试风格使其变得更复杂

这似乎是一个很大的障碍,因为我不仅“不是真正的单元测试”,而且设置测试也很麻烦,因为代码越来越大

我认为您可以忽略“并非真正的单元测试”;重要的是使用适合目的的工具,而不是品牌

这就是说,
设置测试很麻烦
是一个合理的问题,而且这本身就足以成为寻找改进设计方法的借口

如果您的服务与某些第三方实现紧密耦合,而这些第三方实现不提供替代功能,那么您将如何将其与测试分离?通常的答案是在你的代码和第三方代码之间引入一个新的设计元素seam

煤层的两个重要特征:

  • 它确实提供了替代品;也就是说,您有一个接口
  • 与第三方代码集成的接口的实现“非常简单,显然没有缺陷”
然后,在测试中,引入替代实现

与您的“域模型”的游戏完全相同。假设您正在应用通常的生命周期模式,seam包括存储库的替代品和聚合根实体的替代品


一些好消息-您不必对整个聚合进行阴影处理:只需对服务关心的接口部分进行阴影处理。实际上,您正在为每个服务定义描述服务和域模型之间交互的契约。“角色界面”将是一个有用的搜索词。

首先,我将确保满足以下两个条件:

  • 域模型是POJO
  • 域层隔离(其他层可以访问域层,但不能访问其他层)

然后,可以使用Factory、Builder或TestHelper将模型带到期望状态进行测试。

我遇到了类似的挑战,我们与团队一起创建了一个工具,通过使用随机数据生成器简化了测试数据排列过程:。特别是看看:

  • 因为它明确地引用了常见的DDD构建块,并解释了如何围绕它们安排测试数据。在我的例子中,遵循这些建议大大减少了准备测试数据的代码量和复杂性
  • 它展示了如何处理模型不变量
除了测试安排页面上给出的建议外,使用L也很方便