【问题标题】:Is it necessary to Mock every class that is injected into the method?是否有必要模拟注入方法的每个类?
【发布时间】:2018-01-24 16:49:08
【问题描述】:

我的问题和这个类似:Should I mock all the dependencies when unit testing?

我了解当我的代码访问数据库或 Web 服务或文件系统上的文件时模拟的好处。嘲笑意味着如果有一个我无法控制的问题,例如网络问题,那么我的单元测试将通过 - 因为我的代码仍然正确。

但是,假设我有一些这样的代码:

public Offer AssignOffer(OfferType offerType, IOfferValueCalculator valueCalculator) 
        { 
            DateTime dateExpiring = offerType.CalculateExpirationDate(); 
            int value = valueCalculator.CalculateValue(this, offerType); 


            var offer = new Offer(this, offerType, dateExpiring, value); 


            _assignedOffers.Add(offer); 


            NumberOfActiveOffers++; 


            return offer; 
        } 

我从这里拿的:https://github.com/jbogard/presentations/blob/master/WickedDomainModels/After/Model/Member.cs

模拟传递给方法的对象参数,即 OfferType 和 IOfferValueCalculator(我编写的接口和类)是正常的做法吗?这些类是我编写的,与 Member.AssignOffer 在同一个项目中。

我之前问过一个类似的问题;但是它被关闭了,因为基于意见。我试图通过提供示例代码使这个问题更清楚。

我的问题也类似这样:When should I mock?

这个:https://lostechies.com/jimmybogard/2011/01/07/putting-mocks-in-their-place/ 和这个:http://www.taimila.com/blog/ddd-and-testing-strategy/ 似乎只建议模拟:“模拟跨越架构上重要的边界,但不在这些边界内”。

【问题讨论】:

  • 我怀疑它仍然非常基于意见,但也许仍然有价值。问题真的变成了,“这个测试验证了什么?”如果您模拟输入,那么测试至少在某种程度上验证所提供的对象。这些对象可能以某种方式失败,这将导致此测试失败,即使正在测试的特定方法没有失败。 (一个依赖项。)如果这是可以接受的,那么你不需要模拟。但是,如果您想要一种更纯粹的方法来测试此方法,建议使用模拟。
  • 您寻求的答案的性质是基于意见的。嘲讽没有灵丹妙药。当人们谈论单元测试时,他们暗示单元是一个类,但你的单元真的是一个类吗?一个单元可以很容易地成为一个类树,其中只有聚合根很重要。如果没有父组件就没有目的,为什么要模拟它的组件?
  • @Justinas Marozas,我同意。您能否针对我的问题中的代码定制您的评论/答案?即你会模拟 OfferType 和 IOfferValueCalculator 吗?

标签: c# unit-testing mocking


【解决方案1】:

是的,如果您模拟报价类型,IOffervalueCalculator,您可以使用报价类型、到期日期等的不同组合来测试代码的其他部分。

【讨论】:

【解决方案2】:

TL;DR:不,绝对没有必要模拟每个注入的类型。

一般来说,您会在您的单元上下文之外模拟对象。模拟文件系统、数据库等很容易知道,因为您的应用程序与那些外部组件之间的界限非常清晰。

在您的应用程序内部,但它不是黑白的。如果您的组件定义明确并且边界清晰,那么选择要模拟的内容会很容易。如果这些边界没有很好地定义,则很难选择需要模拟的内容,在这种情况下,我建议热切地模拟。定义不明确的上下文边界通常会导致许多依赖项,而不是可能导致脆弱测试的模拟。

在您在问题中选择的代码示例中,在简要查看了 repo 之后,我将模拟 OfferTypeIOfferValueCalculator。我将使用名称的力量来扩展原因:

  • 要测试的类是Member。不知道 属于什么上下文;
    • 这表明上下文边界不明确 - 如果不确定,最好将依赖关系视为外部依赖;
  • 该方法的主要任务是“分配报价”。只要我知道方法按预期使用它,我是否关心外部组件将计算的到期日期?不;
    • 附带问题:为什么分配报价的方法甚至处理到期日期的计算?这个单一的责任如何?
  • 我认为IOfferValueCalculatorMember 属于同一上下文吗?我可能不会嘲笑它。但同样 - 我不这么认为,在“分配报价”问题的背景下,计算值不是一个重要的细节;

总结:使用不同的名称来测试对象和方法以更好地了解应用程序,我的答案可能会有所不同。现在很难推断什么属于哪里,什么应该取决于什么。

在不同的场景中,我会看到一个简洁的集群,其中包含紧密相关的类型,与其他集群的依赖关系很少,我不太愿意模拟。

【讨论】:

  • OfferType、Member 和 ValueCalculator 是同一聚合的一部分。在此基础上,它们是类的逻辑分组?因此,我在不需要模拟的一边播出。你怎么看?
  • 如果是这样的话,我会说你不需要嘲笑他们。如果它们跨越该逻辑分组的边界,您可能仍需要模拟它们的依赖关系。不要把它当作某种硬性规则。总会有例外。
猜你喜欢
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
  • 2021-11-20
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
相关资源
最近更新 更多