使用Moq进行单元测试

时间:2011-06-07 08:50:51

标签: c# unit-testing mocking moq

假设我有一个包含多个函数的普通类:

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),以便仅测试main函数内的代码。我使用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);
    }
}

我知道最新的是最干净的方式,但我有很多函数一个接一个地调用自己,这样就可以编写很多类。

更新

模仿Thomas的回答:

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

请参阅Gishu的回答,以便更全面地了解这个“问题”。

2 个答案:

答案 0 :(得分:3)

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

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

然后你可以模拟或存根那些你不想要这样调用的方法:

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

它需要你的GetTotal方法是虚拟的。

答案 1 :(得分:2)

使用模拟来抽象出麻烦的依赖关系。因此,如果您的班级处理文件,网络,难以控制/感知的事物,您可以将它们抽象出角色/界面。这允许您伪造/模拟角色(将测试与不依赖于测试的实际实现/依赖性隔离开来)。

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

在你的情况下,你的A.Method1在内部调用Method2或Method3,我认为它不是对测试不友好的。将M2从M2和M3中隔离出来的成本太高,以获得微不足道的好处(除非我不理解某些上下文细节)。如果明天你决定重构Method1以使用M4而不是M3,那么即使M1做了它应该做的事情(从功能p.o.v。),你的测试也会失败。

您可以直接针对Method1编写测试,而不会让测试知道实现细节(称为M2和M3)。所以我的评论是隔离对象有时是有用的..但是将相同对象的功能相互隔离是骇人听闻的恕我直言。