难以理解Moq

4

我对Moq框架感到困惑。从Moq网站上给出的示例中...

// Assumptions:
public interface IFoo {
   public bool DoSomething(string);
   public bool TryParse(string, out string));
}

var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);

这里的Moq设置是用来做什么的?
// out arguments
var outString = "ack";
// TryParse will return true, and the out argument will return "ack", lazy evaluated
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);

这里也有同样的问题..正在设置什么?

是否有人知道更深入的Moq指南?我阅读了这篇SO文章,推荐了书籍“使用.NET示例进行单元测试的艺术”,但该书已经相当陈旧并已绝版。


1
据我所知,几年前《单元测试的艺术》出版了第二版,目前仍在印刷中。 - NikolaiDante
@NikolaiDante - 哇!我甚至没有检查过那个。谢谢! - webworm
1个回答

4

如果想了解mocking的一般背景,可以参考这篇答案,它很好地解释了mocking以及为什么你可能想在单元测试中使用它。

具体来说,Moq是一个库,可以让你轻松创建模拟对象并控制它们的行为。最好通过示例来描述,所以让我们看看你的第一个代码示例:

public interface IFoo {
   public bool DoSomething(string);
   public bool TryParse(string, out string));
}

/* 1 */ var mock = new Mock<IFoo>();
/* 2 */ mock.Setup(foo => foo.DoSomething("ping")).Returns(true);

第1行创建了一个IFoo接口的模拟实现(mock implementation)。在后台,Moq使用Castle DynamicProxy库动态创建IFoo的具体实现,然后将其包装在自己的Mock类中,以便我们可以配置它的行为。

现在我们有了一个模拟对象,我们经常需要配置它如何响应其方法的调用,这样我们就可以测试我们的被测系统对其的反应。第2行的Setup方法完成了这个任务,告诉我们的模拟对象当使用参数等于"ping"调用DoSomething时返回true。想象一下您正在使用此模拟对象来测试以下类:

public class TestObject 
{
    public string TestMethod(IFoo foo)
    {
        if (foo.DoSomething("ping"))
            return "ping";
        else if (foo.DoSomething("pong"))
            return "pong";
        return "blah";
    }
}

为了获得完整的测试覆盖率,您需要实现一个IFoo接口,它可以:
  1. 在一个测试中返回true,以响应"ping"
  2. 在另一个测试中返回true,以响应"pong"
  3. 在最后一个测试中,对于任何参数都返回false
您可以创建自己的模拟对象来实现这种行为,例如:
public class MockFoo : IFoo
{
    string trueValue;

    public MockFoo(string trueValue)
    {
        this.trueValue = trueValue;
    }

    public bool DoSomething(string value)
    {
        return value == trueValue;
    }
}

但是当你有复杂的逻辑、多个参数或者许多依赖关系时,这种方式就会变得很麻烦,无法很好地扩展。这就是Mock对象和Moq可以让事情变得简单的地方。在Moq中,三个测试的相同设置如下:

  1. mock.Setup(foo => foo.DoSomething("ping")).Returns(true);
    
  2. mock.Setup(foo => foo.DoSomething("pong")).Returns(true);
    
  3. mock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(false);
    
在我看来,这更简单且更能表达您对IFoo依赖项行为的期望。
至于指南,我认为你最好阅读关于模拟的一般性指南,而不是针对Moq的专门指南。Moq只是一个让使用模拟对象更容易的库,而快速入门是使用Moq的机制的很好的参考。有许多关于模拟的教程和指南,只需搜索一些即可。记住,直到我开始使用它们,才真正“点燃”了我的灵感。找一个编码卡塔并尝试自己模拟!

非常好的解释!非常感谢您抽出时间将所有内容整理在一起!我会采纳您的建议,寻找更多通用的“mocking”教程。 - webworm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接