Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq Verify with anonymous type parameter

I have the following test, with supporting classes, but I can't figure out how to verify the call on the dependency.

[TestFixture]
public class AnonymousGenericTypeParameterTests
{
    [Test]
    public void Test()
    {
        // Arrange
        var dependency = new Mock<IDependency>();

        var towns = new List<Town>
        {
            new Town { Name = "Lifford", County = "Donegal", Country="Ireland", Population = 1658 },
            new Town { Name = "Ballyshannon", County = "Donegal", Country="Ireland", Population = 2504 },
            new Town { Name = "Buxton", County = "Derbyshire", Country="United Kingdom", Population = 13599 },
        };

        var sut = new MyClass(dependency.Object);

        // Act
        sut.DoSomething(towns);

        // Assert
        // The following line needs to be fixed.
        dependency.Verify(d => d.Execute(It.IsAny<IEnumerable<object>>(), It.IsAny<Func<object, decimal?>>()));
    }
}
public interface IDependency
{
    void Execute<T>(IEnumerable<T> collection, Func<T, decimal?> rateSelector);
}
public class MyClass
{
    private readonly IDependency dependency;
    public MyClass(IDependency dependency)
    {
        this.dependency = dependency;
    }
    public void DoSomething(IEnumerable<Town> towns)
    {
        var counties = towns.GroupBy(t => new {t.Country,t.County});
        foreach (var county in counties)
        {
            dependency.Execute(county, c => c.Population);
        }
    }
}
public class Town
{
    public string Name { get; set; }
    public string County { get; set; }
    public int Population { get; set; }
    public string Country { get; set; }
}

According to Moq's test output, the performed invocations are:

Dependency.Execute(System.Linq.Lookup`2+Grouping[<>f__AnonymousType0`2[System.String,System.String],UniFocus.Staffscope4.Tests.Town], System.Func`2[UniFocus.Staffscope4.Tests.Town,System.Nullable`1[System.Decimal]])

I see plenty of questions regarding anonymous parameters in Moq (such as this and this and this), but can't find anything relating to using an anonymous type as the actual type parameter.

What can be put in the Verify line so that it actually verifies the call inside?

Note: My example IDependency doesn't return a value (it's already complex enough, I think), but there will be bonus kudos for answers that implictly or explicitly address Setup() as well as Verify().

Update Jesse's solution only passes the test because I made a bad choice when crafting my example. I should have realised that any IGrouping<out TKey, out TElement> is also an IEnumerable<TElement>. Is there a more universal solution?

Update 2 I feel like my original example was possibly too elaborate and didn't represent well the actual title of my question. Is there any solution that works for this more straightforward and to-the-point example?

using Moq;
using NUnit.Framework;

namespace Tests
{
    [TestFixture]
    public class SimpleAnonymousGenericTypeParameterTests
    {
        [Test]
        public void Test()
        {
            // Arrange
            var dependency = new Mock<IDependency>();
            var sut = new MyClass(dependency.Object);

            // Act
            sut.DoSomething("Donegal", "Lifford");

            // Assert
            // This verify works for both calls to Execute()
            dependency.Verify(d => d.Execute(It.IsAny<object>()), Times.Exactly(2));
            // This verify should specifically refer to only the first call to Execute()
            dependency.Verify(d => d.Execute(It.IsAny</*HowToRepresentAnonymousTypeHere*/object>()), Times.Once);
        }
        public interface IDependency
        {
            void Execute<T>(T thing);
        }
        public class MyClass
        {
            private readonly IDependency dependency;
            public MyClass(IDependency dependency)
            {
                this.dependency = dependency;
            }
            public void DoSomething(string county, string town)
            {
                dependency.Execute(new { county, town });
                object someUnknownObject = "";
                dependency.Execute(someUnknownObject);
            }
        }
    }
}
like image 777
mo. Avatar asked Feb 13 '14 14:02

mo.


2 Answers

The accepted answer doesn't work for me, I believe it's because the tests and the object in question are in a different assembly so Moq doesn't know how to reconcile the types and does not match them.

Instead, I created the following helper methods that can verify that the anonymous type provided has the correct fields and values:

public static class AnonHelpers
{
    public static object MatchAnonymousType(object expected)
    {
        return Match.Create(Matcher(expected));
    }

    private static Predicate<object> Matcher(object expected)
    {
        return actual =>
        {
            var expectedProp = expected.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(expected));
            var actualProp = actual.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(actual));

            foreach (var prop in expectedProp)
            {
                if (!actualProp.ContainsKey(prop.Key))
                    return false;
                if (!prop.Value.Equals(actualProp[prop.Key]))
                    return false;
            }
            return true;
        };
    }
}

They can be used like so:

var anon = new { SomeKey = "some value", SomeOtherKey = 123 };
myMock.Setup(x => x.MyMethod(personIDs, AnonHelpers.MatchAnonymousType(anon))).Verifiable();

This will create a matcher that will use reflection to match the anonymous type based on it's keys and values and then you can use normal verification to see when it's been called.

like image 150
DLeh Avatar answered Oct 07 '22 01:10

DLeh


Since the types are known in the context of the test, you could provide the specific type arguments to the Verify call. The following change got the test to pass:

dependency.Verify(d =>
  d.Execute(It.IsAny<IEnumerable<Town>>(), It.IsAny<Func<Town, decimal?>>()));

The same should also work for setups.

With regards to the example in Update 2, the following passes, but it requires knowledge of the inner workings of the DoSomething() method and as far as I know it's the only way to make it work:

var anonymousType = new {county = "Donegal", town = "Lifford"};
dependency.Verify(d => d.Execute(anonymousType), Times.Once);
like image 44
Jesse Sweetland Avatar answered Oct 07 '22 02:10

Jesse Sweetland