Using Moq can you verify a method call with an anonymous type?

狂风中的少年 提交于 2019-12-21 07:03:45

问题


I'm trying to verify a method call using Moq, but I can't quite get the syntax right. Currently I've go this as my verify:

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

The code compiles, but the test fails with the error:

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

How can I verify the method call and match the method parameters for an anonymous type.

UPDATE

To answer the questions:

I am trying to verify both that the method was called and that the parameters are correct.

The signature of the method I'm trying to verify is:

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

The setup code is simply:

repository = new Mock<IRepository>();

UPDATE 2

It looks like this is a problem with Moq and how it handles anonymous types in .Net. The code posted by Paul Matovich runs fine, however, once the code and the test are in difference assemblies the test fails.


回答1:


This Passes

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

Update

That is strange, it doesn't work in different assemblies. Someone can give us the long definition about why the object.equals from different assemblies behaves differently, but for different assemblies this will work, any variance in the object values will return a different hash code.

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



回答2:


One option is to "verify" it in a Callback. Obviously this needs to be done at Setup time, e.g.:

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



回答3:


None of the answers are great when your test assembly is different than the system under test's assembly (really common). Here's my solution that uses Json serialization and then string comparison.

Test Helper Function:

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

Example System Under Test:

public void DoWork(string input)
{
     var obj = new { Prop1 = input };
     dependency.SomeDependencyFunction(obj);
}

Example Unit Test:

var expectedObject = new { Prop1 = "foo" };

sut.DoWork("foo");
dependency.Verify(x => x.SomeDependencyFunction(It.Is<object>(y => VerifyHelper.AreEqualObjects(expectedObject, y))), Times.Once());

This solution is really simple, and I think makes the unit test easier to understand as opposed to the other answers in this thread. However, because it using simple string comparison, the test's anonymous object has to be setup exactly the same as the system under test's anonymous object. Ergo, let's say you only cared to verify the value of a single property, but your system under test sets additional properties on the anonymous object, your unit test will need to set all those other properties (and in the same exact order) for the helper function to return true.



来源:https://stackoverflow.com/questions/9892362/using-moq-can-you-verify-a-method-call-with-an-anonymous-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!