用最小起订量模拟代表

Mocking delegates with Moq

我有一个界面:

public interface IRepeater
{
    void Each(string path, Action<string> action);
}

我想使用 Moq 模拟此界面。现在我显然可以做到以下几点:

var mock = new Mock<IRepeater>();
mock.Setup(m => m.Each(It.IsAny<string>(), It.IsAny<Action<string>>());

但是,为了帮助测试,我希望能够模拟传递给 Action<string>string。这可以用 Moq 来完成吗?如果是,如何?

更新

澄清一下,我正在测试另一个 class,它依赖于 IRepeater。我想模拟 IRepeater.Each 这样我就可以控制 Action 得到的 string 这样我就可以测试行为了。

例子

所以如果我有一个像这样的class

public class Service
{
    private readonly IRepeater _repeater;

    public Service(IRepeater repeater)
    {
        _repeater = repeater;
    }

    public string Parse(string path)
    {
        var builder = new StringBuilder();

        _repeater.Each(path, line => builder.Append(line));

        return builder.ToString();
    }
}

如何模拟 IRepeater.Each 以便我可以测试 Service.Parse

您似乎想要验证传递的操作(委托)是否将传递给 IRepeater 调用。因为您不是在测试 Repeater,而是 Repeater 调用者(Repeater 是模拟对象,而不是测试对象)。

这是我的做法:

public class Service
{
    private readonly IRepeater repeater;

    public Service(IRepeater repeater)
    {
        this.repeater = repeater;
    }

    public void Foo(string str, Action<string> action)
    {
        repeater.Each(str, action);
    }
}

public class ActionImplement
{
    public virtual void Action(string str)
    {
        Console.Write(str);
    }
}

public interface IRepeater
{
    void Each(string path, Action<string> action);
}

并且测试将验证 ActionImplement.Action

是否通过
    [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var actionImplement = new Mock<ActionImplement>();
        actionImplement.Setup(m => m.Action(It.IsAny<string>()));

        var mock = new Mock<IRepeater>();
        mock.Setup(m => m.Each(It.IsAny<string>(), actionImplement.Object.Action));


        var srv = new Service(mock.Object);
        srv.Foo("aa",actionImplement.Object.Action);


        mock.Verify(ai => ai.Each("aa", actionImplement.Object.Action));


    }

你必须使用callback方法。由于 line => builder.Append(line) 是方法行为的一部分,因此在测试方法时必须执行此行为:

    [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var fakeReporter = new Mock<IRepeater>();

        fakeReporter.Setup(x => x.Each(It.IsAny<string>(), It.IsAny<Action<string>>()))
            .Callback<string, Action<string>>((s, action) => action(s));

        var target = new Service(fakeReporter.Object);

        var result = target.Parse("asdfghj");

        Assert.AreEqual("asdfghj", result);
    }

测试此方法的另一种方法是验证该方法是使用正确的路径调用的,然后验证操作是否正确:

     [TestMethod]
    public void Test_Service_When_Passing_String_And_ActionDelegate()
    {
        var fakeReporter = new Mock<IRepeater>();

        fakeReporter.Setup(x => x.Each(It.IsAny<string>(), It.IsAny<Action<string>>()))
            .Callback<string, Action<string>>((s, action) =>
            {
                Assert.AreEqual("asdfghj", s);
                foreach (var w in "pass")
                {
                    action(w.ToString());
                }
            });

        var target = new Service(fakeReporter.Object);

        var result = target.Parse("asdfghj");

        Assert.AreEqual("pass", result);
    }

顺便说一句,您可以用字符串替换 It.IsAny<string>(),然后删除 Assert.AreEqual("asdfghj", s);(我只是想以明确的方式测试事物...)