模拟框架如何工作?

时间:2010-02-16 22:58:47

标签: unit-testing mocking

如果我要写一个模拟库,这将如何工作(换句话说,“它们如何工作?”?

我想知道的一件事是你总是设定期望,所以你需要将期望与方法在运行时所做的比较,所以我假设需要反射(在运行时解析类型)。

此外,当使用术语“模拟对象”时,对象是否已被删除,或者它是否是具有预设期望的对象?

当我想我将如何编写自己的框架/技术实现时,比如模拟对象,我意识到我真正了解(或者不知道)以及我会绊倒的内容:如果模拟对象是预编程返回设定的期望而你不调用实际的真实对象,那么结果总是不一样吗?例如:

[TestMethod, Isolated]
public void FakeReturnValueByMethodArgs()
{
    var fake = Isolate.Fake.Instance<ClassToIsolate>();
    // MethodReturnInt will return 10 when called with arguments 3, "abc"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3,   "     abc")).WithExactArguments().WillReturn(10);
// MethodReturnInt will return 50 when called with arguments 3, "xyz"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3, "xyz")).WithExactArguments().WillReturn(50);

     Assert.AreEqual(10, fake.MethodReturnInt(3, "abc"));
    Assert.AreEqual(50, fake.MethodReturnInt(3, "xyz"));

}

这总不会真的回归吗?

4 个答案:

答案 0 :(得分:8)

使用模拟框架的想法是模拟依赖项,而不是测试中的实际类。对于您的示例,您的测试将始终返回true,因为实际上您只是测试模拟框架而不是您的实际代码!

真实世界的模拟看起来更像是这样:

[TestMethod, Isolated]
public void FakeReturnValueByMethodArgs() {
    var fake = Isolate.Fake.Instance<DependencyClass>();
    // MethodReturnInt will return 10 when called with arguments 3, "abc"
    Isolate.WhenCalled(()=> fake.MethodReturnInt(3, "abc")).WithExactArguments().WillReturn(10);

    var testClass = new TestClass(fake);
    testClass.RunMethod();

    // Verify that the setup methods were execute in RunMethod()
    // Not familiar with TypeMock's actual method to do this...
    IsolatorExtensions.VerifyInstanceWasCalled(fake);  

    // Or assert on values
    Assert.AreEqual(10, testClass.AProperty);
}

注意如何将mock传递给TestClass并在其上运行方法。

您可以阅读The Purpose of Mocking以更好地了解模拟是如何工作的。


更新:解释为什么您只测试模拟框架:

您所做的是使用MethodReturnInt创建一个带有模拟框架的方法Isolate.WhenCalled()。当您在Assert中调用MethodRecturnInt时,代码将运行委托() => fake.MethodReturnInt()并返回10.模拟框架有效地创建了一个方法(尽管是动态的),看起来像这样:

public void MethodReturnInt(int value, string value2) {
    Assert.Equal(3, value);
    Assert.Equal("abc", value2);
    return 10;
}

这比这复杂一点,但这是一般的想法。既然你从不运行任何代码而不是创建2个方法然后在这两个方法上断言,那么你就不会测试自己的代码,因此只测试模拟框架。

答案 1 :(得分:0)

是的,它总是会返回true。当被测试的类需要另一个您不想参与测试运行的类实现时,应该使用Mock对象。当它是一个使用多个实现的接口的类,或者您不想设置的复杂/昂贵/外部服务时,这是最有用的。

在上面的代码中,您正在嘲笑您正在“测试”的类。

另一种思考方式是你记录的模拟行为是黑盒子(实现)断言,其中Assert.*是白盒子(api)断言。

答案 2 :(得分:0)

你有正确的想法。您经常会发现它们有两种操作模式。如果你担心你的方法没有被调用或者没有按正确的顺序调用,那么通常会有一个'严格'模式导致模拟框架抛出异常,如果方法没有被调用到测试,或用错误的参数调用等。

大多数框架都考虑过这些问题,因此您只需要了解如何为您的方案配置它。

答案 3 :(得分:0)

查看模拟系统如何工作的一种方法只是看你需要一个对象的时候,但是你不想使用真正的类,而是希望它给你一些特定类型的数据,它不会(或不会可靠地这样做)。所以,如果你看到:

Assert.IsTrue(myLogic.IsNoon(time))

你可以看到断言如何让时间对象永远是中午。 。 。好吧,你不能可靠地使用真实对象。所以你需要一个替身。你可以为测试制作一个假类,但这有点沉重。模拟框架是一种捷径。

相关问题