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


我正在尝试使用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 })

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

UPDATE

回答问题:

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

我要验证的方法的签名是:

int ExecuteNonQuery(string query, object param = null);

设置代码很简单:

repository = new Mock<IRepository>();

更新2

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


6468
2018-03-27 15:12


起源

你也可以发布设置代码吗? - Iridio
您是否可以发布您要验证的方法调用的签名,以及是否尝试验证传递给该方法的参数,或者仅在调用它时? - Paul Matovich
Rob我测试了Pauls更新的答案,它适用于不同的程序集。也许你应该接受他的回答。 - Rok


答案:


这通过

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

9
2018-03-28 19:59



对象equals的工作方式不同,因为如果对象的类型来自不同的程序集,则表示不同的名称空间。另请注意,匿名类型会覆盖Equals和GetHashCode方法。看到这个 链接 更多细节。 - Rok
感谢Rok提供的额外信息以及我的回答。 :-) - Paul Matovich
从 MS Docs  - 如果程序集中的两个或多个匿名对象初始值设定项指定了具有相同顺序且具有相同名称和类型的属性序列,则编译器会将对象视为相同类型的实例。它们共享相同的编译器生成的类型信息。 - Dimitar Tsonev


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

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

5
2018-03-18 15:51



我的单元测试是在不同的程序集中。为了完成这项工作,我将以下内容添加到正在测试的项目中的Assembly.cs中 - [assembly:InternalsVisibleTo(“UnitTest”)]。在此更改之前,我收到以下错误:Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:'object'不包含'Name'的定义 - ministrymason