用最小起订量模拟代表
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);
(我只是想以明确的方式测试事物...)
我有一个界面:
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);
(我只是想以明确的方式测试事物...)