代码之家  ›  专栏  ›  技术社区  ›  Andrew

使用moq重写同一类中的虚拟方法

  •  27
  • Andrew  · 技术社区  · 14 年前

    我们正在使用moq对服务类进行单元测试,但是在如何测试一个服务方法调用同一类的另一个服务方法的情况上,我们陷入了困境。我尝试将被调用的方法设置为virtual,但仍然无法确定在moq中应该做什么。例如:

    public class RenewalService : IRenewalService
    {
        //we've already tested this
        public virtual DateTime? GetNextRenewalDate(Guid clientId)
        {
            DateTime? nextRenewalDate = null;
            //...<snip> a ton of already tested stuff...
    
            return nextRenewalDate;
        }
    
        //but want to test this without needing to mock all 
        //the methods called in the GetNextRenewalDate method
        public bool IsLastRenewalOfYear(Renewal renewal)
        {
            DateTime? nextRenewalDate = GetNextRenewalDate(renewal.Client.Id);
            if (nextRenewalDate == null)
                throw new Exceptions.DataIntegrityException("No scheduled renewal date, cannot determine if last renewal of year");
            if (nextRenewalDate.Value.Year != renewal.RenewDate.Year)
                return true;
            return false;
        }
    }
    

    在上面的例子中,我们的getNextrenewaldate方法相当复杂,我们已经对它进行了单元测试。但是,我们希望测试更简单的IslastRenewalOfyear,而不需要模拟GetNextrenewaldate所需的一切。基本上,我们只是想模仿下一步。

    我意识到我可以创建一个重写GetNextRenewaldate并测试新类的新类,但是有没有一种方法可以利用MoQ使这个过程更简单?

    3 回复  |  直到 6 年前
        1
  •  45
  •   Beep beep    14 年前

    您可能在此场景中使用部分模拟,尽管您的所有方法都需要是虚拟的:

        var mock = new Moq.Mock<RenewalService>();
        mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
        mock.CallBase = true;
        var results = mock.Object.IsLastRenewalOfYear(...);
    
        2
  •  2
  •   abatishchev Karl Johan    14 年前
    var mock = new Moq.Mock<RenewalService> { CallBase = true };
    mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
    var results = mock.Object.IsLastRenewalOfYear(...);
    
        3
  •  1
  •   Merlyn Morgan-Graham    14 年前

    编辑 :我现在同意这不是安德鲁案件的正确答案。我想把这个答案留在这里作为评论线索。请不要再投反对票了:)

    编辑前:

    通常,模拟对象框架的设计并不是为了简化单个类场景,而是为了隔离代码,以便您可以测试单个类。

    如果您试图使用模拟对象框架来解决这个问题,那么框架将创建一个派生类并重载该方法。唯一不同的是,您可以在大约3行而不是5行中完成这项工作,因为您不必创建派生类定义。

    如果您想使用模拟对象来隔离这个行为,那么您应该将这个类拆分一下。这个 GetNextRenewalDate 逻辑可能存在于 RenewalService 对象。

    您遇到这个问题的事实可能表明有一个更简单或更细粒度的设计尚待发现。找到一个不那么具体的类,它的名称类似于“manager”或“service”,这通常意味着您可以将设计分解为更小的类,并从中获得更好的可重用性和可维护性。