Moq的单元测试

本文关键字:单元测试 Moq | 更新日期: 2023-09-27 17:58:45

假设我有一个带有几个函数的普通类:

public class MyClass
{
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return GetTotal(myValue);
        else
            return GetTotal(myString);
    }
    public int GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }
    public int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}

我想对我的第一个函数进行单元测试,并"模拟"其他函数int GetTotal(int myValue)int GetTotal(string myString),以便只测试主函数中的代码。我使用Moq作为嘲讽的框架。是否有一些技巧可以让我从要测试的函数中获取代码,并模拟对其他函数的内部调用?或者我应该调用第二个像这样的对象来模拟一切吗?

public class MyCalculator
{
    public int GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }
    public int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}
public class MyClass
{
    MyCalculator _Calculator;
    public MyClass(MyCalculator calculator) { _Calculator = calculator; }
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return _Calculator.GetTotal(myValue);
        else
            return _Calculator.GetTotal(myString);
    }
}

我知道最新的方法是最干净的,但我有很多函数一个接一个地调用自己,所以这会导致很多类需要编写。

更新

托马斯答案的模拟实现:

public class MyClass
{
    public int GetTotal(int myValue, string myString)
    {
        if (myValue > 10)
            return GetTotal(myValue);
        else
            return GetTotal(myString);
    }
    public virtual int  GetTotal(int myValue)
    {
        return myValue * 25 / 12 + 156;
    }
    public virtual int GetTotal(string myString)
    {
        return myString.Length * 25 / 48 + 27;
    }
}
[TestClass]
public class Test
{
    [TestMethod]
    public void MyClass_GetTotal()
    {
        Mock<MyClass> myMockedClass = new Mock<MyClass>() {CallBase = true};
        myMockedClass.Setup(x => x.GetTotal(It.IsAny<int>())).Returns(1);
        myMockedClass.Setup(x => x.GetTotal(It.IsAny<string>())).Returns(2);
        var actual = myMockedClass.Object.GetTotal(0,string.Empty);
        Assert.AreEqual(2,actual);
    }
}

更新2

关于这个"问题"的更多全球视角,请参阅吉书的回答

Moq的单元测试

当然!在Rhino Mocks中,您可以使用部分mock来达到此目的。创建一个新的模拟像这样:

var mock = MockRepository.GeneratePartialMock<MyClass>();

然后,您可以模拟或存根这两个您不想这样调用的方法:

mock.Stub(x => x.GetTotal(10)).Return(42);

不过,它要求GetTotal方法是虚拟的。

您使用mock来抽象掉麻烦的依赖关系。因此,如果你的类处理文件、网络、难以控制/感知的事情,你可以将它们抽象到角色/界面后面。这允许您伪造/模拟角色(将您的测试与对测试不友好的真实实现/依赖隔离开来)。

因此,如果A依赖于B,而B很难控制/感知,或者只是速度慢。我们把B放在角色R后面,然后模拟R进行单元测试a.

在您的情况下,A.Method1在内部调用Method2或Method3,我认为这对测试并不友好。将M1与M2和M3隔离开来对IMHO来说成本太高而收益甚微(除非我不了解一些上下文细节)。如果明天你决定重构Method1以使用M4而不是M3,即使M1做了它应该做的事情,你的测试也会失败(从功能p.o.v.)

您可以直接针对Method1编写测试,而不让测试知道实现细节(它调用M2和M3)。所以我的评论是,隔离对象有时很有用。。但是将同一对象的功能彼此隔离是过度扼杀IMHO。