使用Moq覆盖同一类中的虚方法

时间:2010-08-21 22:25:31

标签: c# unit-testing moq

我们正在使用Moq对我们的服务类进行单元测试,但是他们仍然坚持如何测试服务方法调用同一类的另一个服务方法的情况。我尝试将被调用的方法设置为虚拟,但仍然无法弄清楚在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。

我意识到我可以创建一个覆盖GetNextRenewalDate并测试新类的新类,但有没有办法可以利用Moq来简化这个?

3 个答案:

答案 0 :(得分:45)

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

    var mock = new Moq.Mock<RenewalService>();
    mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
    mock.CallBase = true;
    var results = mock.Object.IsLastRenewalOfYear(...);

答案 1 :(得分:2)

var mock = new Moq.Mock<RenewalService> { CallBase = true };
mock.Setup(m => m.GetNextRenewalDate(It.IsAny<Guid>())).Returns(null);
var results = mock.Object.IsLastRenewalOfYear(...);

答案 2 :(得分:1)

编辑:我现在同意这不是安德鲁案件的正确答案。我想在这里为评论主题留下这个答案。请不要再投票了:)

编辑前:

通常,模拟对象框架并非旨在简化单个类方案,它们旨在隔离您的代码,以便您可以测试单个类。

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

如果您想使用模拟对象来隔离此行为,那么您应该稍微拆分此类。 GetNextRenewalDate逻辑可能位于RenewalService对象之外。

您遇到此问题的事实可能表明有一个更简单或更精细的设计尚待发现。找到一个不那么具体的类,使用“经理”或“服务”之类的名称通常暗示您可以将您的设计分解为更小的类,并从中获得更好的可重用性和可维护性。