使用Moq可以验证匿名类型的方法调用吗?

ili*_*ian 15 moq

我正在尝试使用Moq验证方法调用,但我无法正确地获得语法.目前我已将此作为我的验证:

repository.Verify(x => x.ExecuteNonQuery("fav_AddFavorites", new
{
    fid = 123,
    inputStr = "000456"
}), Times.Once());
Run Code Online (Sandbox Code Playgroud)

代码编译,但测试失败并出现错误:

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 })
Run Code Online (Sandbox Code Playgroud)

如何验证方法调用并匹配匿名类型的方法参数.

UPDATE

回答问题:

我试图验证方法被调用和参数是否正确.

我试图验证的方法的签名是:

int ExecuteNonQuery(string query, object param = null);
Run Code Online (Sandbox Code Playgroud)

设置代码很简单:

repository = new Mock<IRepository>();
Run Code Online (Sandbox Code Playgroud)

更新2

看起来这是Moq的一个问题,以及它如何处理.Net中的匿名类型.Paul Matovich发布的代码运行良好,但是,一旦代码和测试在不同的程序集中,测试就会失败.

Pau*_*ich 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());
    }
Run Code Online (Sandbox Code Playgroud)

更新

这很奇怪,它在不同的程序集中不起作用.有人可以给我们详细说明为什么来自不同程序集的object.equals的行为不同,但对于不同的程序集,这将起作用,对象值的任何差异都将返回不同的哈希代码.

        Class2Stub.Verify(x => x.ExecuteNonQuery(testString, It.Is<object>(o => o.GetHashCode() == (new { fid = 123, inputStr = "000456" }).GetHashCode())), Times.Once());
Run Code Online (Sandbox Code Playgroud)


Bor*_*yll 7

当您的测试组件与被测系统的组件不同时(非常常见),所有答案都不是很好。这是我的解决方案,它使用 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

被测系统示例:

public void DoWork(string input)
{
     var obj = new { Prop1 = input };
     dependency.SomeDependencyFunction(obj);
}
Run Code Online (Sandbox Code Playgroud)

单元测试示例:

var expectedObject = new { Prop1 = "foo" };
    
sut.DoWork("foo");
dependency.Verify(x => x.SomeDependencyFunction(It.Is<object>(y => VerifyHelper.AreEqualObjects(expectedObject, y))), Times.Once());
Run Code Online (Sandbox Code Playgroud)

这个解决方案非常简单,我认为与该线程中的其他答案相比,单元测试更容易理解。但是,由于它使用简单的字符串比较,因此测试的匿名对象必须与测试的匿名对象下的系统设置完全相同。因此,假设您只关心验证单个属性的值,但是您的测试系统在匿名对象上设置了其他属性,您的单元测试将需要为该对象设置所有其他属性(并且以相同的顺序)辅助函数返回 true。


mor*_*ger 6

一种选择是在回调中"验证"它.显然,这需要在安装时完成,例如:

aMock.Setup(x => x.Method(It.IsAny<object>())).Callback<object>(
  (p1) =>
    {
      dynamic o = p1;
      Assert.That(o.Name, Is.EqualTo("Bilbo"));
    });
Run Code Online (Sandbox Code Playgroud)

  • 我的单元测试位于不同的程序集中。为了完成这项工作,我将以下内容添加到正在测试的项目中的 Assembly.cs - [程序集:InternalsVisibleTo("UnitTest")]。在此更改之前,我收到以下错误: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:“对象”不包含“名称”的定义 (2认同)