24

I'm trying to verify a method call using Moq, but I can't quite get the syntax right. Currently, I've got 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 different assemblies the test fails.

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
ilivewithian
  • 19,476
  • 19
  • 103
  • 165
  • Can you also post the setup code? – Iridio Mar 28 '12 at 13:52
  • Can you post the signature of the method call you are trying to verify and if you are trying to verify the parameters passed to the method as well or only if it was called? – Paul Matovich Mar 28 '12 at 19:15
  • Rob I tested Pauls updated answer and it works with different assemblies. Maybe you should accept his answer. – Rok Dec 06 '12 at 20:07

4 Answers4

19

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());
Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
Paul Matovich
  • 1,496
  • 15
  • 20
  • 1
    Object equals works differently because the type of object is different if it comes from different assembly, meaning different namespace. Also note that anonymous types override the Equals and GetHashCode methods. See this [link](http://stackoverflow.com/questions/543482/linq-select-distinct-with-anonymous-types) for more details. – Rok Dec 06 '12 at 19:53
  • Thanks Rok for the extra information and for the uptick on my answer. :-) – Paul Matovich Dec 07 '12 at 23:51
  • From [MS Docs](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types) - If two or more anonymous object initializers in an assembly specify a sequence of properties that are in the same order and that have the same names and types, the compiler treats the objects as instances of the same type. They share the same compiler-generated type information. – Dimitar Tsonev Aug 07 '17 at 13:54
8

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"));
    });
moreginger
  • 530
  • 1
  • 5
  • 10
  • 2
    My unit tests are in a different assembly. To make this work I added the following to Assembly.cs in the project being tested - [assembly: InternalsVisibleTo("UnitTest")]. Before this change i was getting the following error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : 'object' does not contain a definition for 'Name' – ministrymason Sep 14 '18 at 10:27
7

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 strings 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 set up exactly the same as the system under the 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.

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
Borophyll
  • 1,099
  • 2
  • 15
  • 24
  • Using json serializers may not be everybody's cup of tea, but you have a point about simplicity and readability here. This technique works really well. Thanks ! – Sbu Sep 22 '21 at 12:50
1

I created a reusable method based on Pauls answer:

object ItIsAnonymousObject(object value)
{
    return It.Is<object>(o => o.GetHashCode() == value.GetHashCode());
}

...

dependency.Verify(
    x => x.SomeDependencyFunction(ItIsAnonymousObject(new { Prop1 = "foo" })),
    Times.Once());

Also, this can be used for property name case-insensitive comparison:

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));
}
Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
maxc137
  • 2,291
  • 3
  • 20
  • 32