用Moq模拟代表

本文关键字:模拟 Moq | 更新日期: 2023-09-27 18:27:24

我有一个接口:

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

用Moq模拟代表

您必须使用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);(我只是喜欢用显式的方式测试…)

似乎您希望验证传递的Action(委托)是否会传递给IRepeater调用。因为你测试的不是中继器,而是中继器调用方(中继器是模拟的,而不是测试对象)。

以下是我应该如何做到的:

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));

    }