Unit testing Don';模拟域对象规则?
我目前团队中最有经验的开发人员已经根据我们应该遵循的最佳实践制定了一些硬规则。其中包括“你永远不会模拟域对象”。我问他为什么我们不能,但他从来没有时间给我一个正确的答案。现在他离开了一个星期,我对这条规则的不信任达到了顶峰,这是我的情况 我的域对象有一个Unit testing Don';模拟域对象规则?,unit-testing,domain-driven-design,Unit Testing,Domain Driven Design,我目前团队中最有经验的开发人员已经根据我们应该遵循的最佳实践制定了一些硬规则。其中包括“你永远不会模拟域对象”。我问他为什么我们不能,但他从来没有时间给我一个正确的答案。现在他离开了一个星期,我对这条规则的不信任达到了顶峰,这是我的情况 我的域对象有一个Update方法,其中有几个参数,其中一个是计算器的接口。然后,它更新几个字段,运行计算器,并将其结果分配给其他一些字段 Update方法本身的适当单元测试相当长 现在我有了一些代码,可以做一些事情,然后对这样一个域对象调用Update 我通常会
Update
方法,其中有几个参数,其中一个是计算器的接口。然后,它更新几个字段,运行计算器,并将其结果分配给其他一些字段
Update
方法本身的适当单元测试相当长
现在我有了一些代码,可以做一些事情,然后对这样一个域对象调用Update
我通常会模拟对象,只需检查是否使用正确的参数调用了Update
方法。但是现在,我必须通过检查它的字段和模拟计算器来测试它是否被正确调用,就像我在单元测试Update
方法本身时所做的那样。我必须在调用Update
方法的任何地方这样做。
当Update
方法稍有改变,而这些测试突然中断并需要重构时,这将是多么有趣啊。。。我觉得这条规则就像是朝自己的脚开枪
所以我需要知道,为什么你从不模仿域对象“
您从未模拟过域对象
这更像是一种启发,而不是一种最佳实践。我可以想象它在一些项目中是有意义的
这种启发式方法有助于进行更好的测试。好的测试易于编写,易于阅读,一旦出现问题就会中断。如果您模拟域模型,您可能还必须模拟它的行为。如果您的模型中有一些复杂的行为,模拟它将非常耗时。测试也将更加脆弱(预测域模型输出时可能会出错)
另一种选择是进行社交单元测试。这意味着要测试的单元将不是孤立的单个类,而是一个类及其某些依赖项。换句话说,您只需在测试中使用域模型的具体对象
我通常会模拟对象,只是检查Update方法是否被正确的参数调用。但是现在,我必须通过检查其字段和模拟计算器来测试它是否被正确调用,就像我在单元测试Update方法本身时所做的那样
检查是否调用了update方法并不一定意味着测试成功。这可能不是您要检查服务是否正常工作的内容。如果您通过了模型的一个具体类(包括计算器),则不必进行任何无用的检查或重复其他测试中所做的操作
此解决方案与单独测试类之间的一个区别是,当域模型中存在bug时,许多其他测试都会失败。但我认为这是完全正确的,因为它无论如何都必须被修复。而Update()
可能不像您从富域模型中期望的那样是一种量身定制的方法——这可能会对测试脆弱性产生影响——我同意您应该采取“永不做X”的建议
如果您理解所有的含义,并且知道集成测试版本的可维护性较差,那么一定要使用mock。在您的上下文中,您,而不是笼统的语句或货物崇拜者,是判断什么对您的系统更好的最终评判者
您从未模拟过域对象
避免模拟域对象可能有不同的原因:
Update
的事实可以通过状态更改轻松检查
Update
方法稍有改变,而这些测试突然中断并需要重构时,这将是多么有趣啊
评论中已经提到,可能更新
方法有很多责任,这就是为什么在很多用例中使用它
在定义被测系统(sut)时有不同的口味。如果您倾向于测试类,那么您经常会发现自己在使用模拟库。如果您倾向于测试场景或行为,那么您很可能只关心状态更改
您可能可以为您的域对象探索不同的设计:尝试将
Update
方法拆分为显式的特定于域的方法,这样每个客户端都会触发不同的行为“对Update
方法的测试甚至可能变得过时,因为它将被其他测试测试。主要问题很可能是Update
方法本身,它试图一次完成太多的用例<代码>“我通常会模拟对象,只检查是否使用正确的参数调用了Update方法”:这并不能告诉您程序的正确性。我很难理解这个正确性定义。对我来说,经过良好单元测试的代码提供了清晰性和推力。更新的单元测试应该显示更新的功能