我正在尝试使用Moq验证方法调用,但是我无法正确理解语法。目前我已将此作为验证:
repository.Verify(x => x.ExecuteNonQuery("fav_AddFavorites", new
{
fid = 123,
inputStr = "000456"
}), Times.Once());
代码编译,但测试失败并显示错误:
Expected invocation on the mock once, but was 0 times:
x => x.ExecuteNonQuery("fav_AddFavorites", new <>f__AnonymousType0<Int32, String>(123, "000456"))
No setups configured.
Performed invocations:
IRepository.ExecuteNonQuery("fav_AddFavorites", { fid = 123, inputStr = 000456 })
如何验证方法调用并匹配匿名类型的方法参数。
更新
回答问题:
我正在尝试验证方法是否被调用以及参数是否正确。
我要验证的方法的签名是:
int ExecuteNonQuery(string query, object param = null);
设置代码简单:
repository = new Mock<IRepository>();
更新2
看起来这是Moq的一个问题,以及它如何处理.Net中的匿名类型。 Paul Matovich发布的代码运行正常,但是,一旦代码和测试在不同的程序集中,测试就会失败。
答案 0 :(得分:10)
此通行证
public class Class1
{
private Class2 _Class2;
public Class1(Class2 class2)
{
_Class2 = class2;
}
public void DoSomething(string s)
{
_Class2.ExecuteNonQuery(s, new { fid = 123, inputStr = "000456" });
}
}
public class Class2
{
public virtual void ExecuteNonQuery(string s, object o)
{
}
}
/// <summary>
///A test for ExecuteNonQuery
///</summary>
[TestMethod()]
public void ExecuteNonQueryTest()
{
string testString = "Hello";
var Class2Stub = new Mock<Class2>();
Class1 target = new Class1(Class2Stub.Object);
target.DoSomething(testString);
Class2Stub.Verify(x => x.ExecuteNonQuery(testString, It.Is<object>(o => o.Equals(new { fid = 123, inputStr = "000456" }))), Times.Once());
}
这很奇怪,它在不同的程序集中不起作用。有人可以给我们一个很长的定义,说明为什么来自不同程序集的object.equals行为不同,但对于不同的程序集,这将起作用,对象值的任何变化都将返回不同的哈希代码。
Class2Stub.Verify(x => x.ExecuteNonQuery(testString, It.Is<object>(o => o.GetHashCode() == (new { fid = 123, inputStr = "000456" }).GetHashCode())), Times.Once());
答案 1 :(得分:6)
一种选择是在回调中“验证”它。显然,这需要在设置时完成,例如:
aMock.Setup(x => x.Method(It.IsAny<object>())).Callback<object>(
(p1) =>
{
dynamic o = p1;
Assert.That(o.Name, Is.EqualTo("Bilbo"));
});
答案 2 :(得分:0)
当您的测试组件与被测系统的组件不同(真的很常见)时,没有一个答案是很好的。这是我的使用Json序列化然后进行字符串比较的解决方案。
测试助手功能:
using Newtonsoft.Json;
public static class VerifyHelper
{
public static bool AreEqualObjects(object expected, object actual)
{
var expectedJson = JsonConvert.SerializeObject(expected);
var actualJson = JsonConvert.SerializeObject(actual);
return expectedJson == actualJson;
}
}
正在测试的示例系统:
public void DoWork(string input)
{
var obj = new { Prop1 = input };
dependency.SomeDependencyFunction(obj);
}
单元测试示例:
var expectedObject = new { Prop1 = "foo" };
sut.DoWork("foo");
dependency.Verify(x => x.SomeDependencyFunction(It.Is<object>(y => VerifyHelper.AreEqualObjects(expectedObject, y))), Times.Once());
此解决方案非常简单,与该线程中的其他答案相比,我认为使单元测试更易于理解。但是,由于它使用简单的字符串比较,因此必须将测试的匿名对象设置为与被测系统的匿名对象完全相同。好吧,假设您只想验证一个属性的值,但是被测系统在匿名对象上设置了其他属性,因此单元测试将需要为所有其他属性(以及相同的顺序)设置所有其他属性。辅助函数返回true。
答案 3 :(得分:0)
我根据 Pauls 的回答创建了一个可重用的方法:
object ItIsAnonymousObject(object value)
{
return It.Is<object>(o => o.GetHashCode() == value.GetHashCode());
}
...
dependency.Verify(
x => x.SomeDependencyFunction(ItIsAnonymousObject(new { Prop1 = "foo" })),
Times.Once());
此外,这可用于属性名称不区分大小写的比较:
protected object ItIsAnonymousObject(object value)
{
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
return It.Is<object>(o => JsonSerializer.Serialize(o, options) == JsonSerializer.Serialize(value, options));
}