9

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);
            }
        }
    }
}
Community
  • 1
  • 1
mo.
  • 4,165
  • 3
  • 34
  • 45

2 Answers2

10

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.

DLeh
  • 23,806
  • 16
  • 84
  • 128
  • I guess this will fail if the anonymously typed object is complex e.g. `new { prop = new { nested = 7 } }` It also only looks like it checks the expected props... so if your actual object has additional props it won't fail? I really like the direction but need to plug a few holes before I think it will fully work. – Lee Tickett Apr 27 '22 at 07:04
7

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);
mo.
  • 4,165
  • 3
  • 34
  • 45
Jesse Sweetland
  • 1,594
  • 11
  • 10
  • Was trying to figure out how this passed. `Execute()` is called like this: `dependency.Execute(county, c => c.Population);`. When I mouse over `county` in there, I see that its type is `IGrouping<'a,Town>` where `'a is new { string Country, string County }`. So how is Moq seeing that `Execute()` was passed an `IEnumerable`? Then it occurred to me, an `IGrouping` is also an `IEnumerable`: `public interface IGrouping : IEnumerable, IEnumerable`. So your edit only passes because I chose a bad type (IGrouping) to use as an example :( – mo. Feb 19 '14 at 21:53
  • DoSomething groups an IEnumerable to produce an IEnumerable>. DoSomething then iterates over that IEnumerable> and calls IDependency.Execute on each IGrouping element. TKey doesn't appear to be important--county.Key is never referenced, so the anonymous object never comes into play other than as a grouping key). IDependency.Execute expects T to be supplied by the caller, which in this example will always be Town passed by DoSomething. Is there some other variation of DoSomething that needs to be supported? – Jesse Sweetland Feb 19 '14 at 22:15
  • You've given me insight that resolves the example above, and resolves the original issue I had in my production code, so thank you very much! However I feel like it's a workaround to the original question in that my anonymous type (`IGrouping<'a,Town>`) implements a non-anymous type (`IEnumerable`) so it's easy to verify it against that. Before I accept this as The Answer, could you take a look at my second update to the question, where my example seems to more accurately reflect the title of the question? I'm starting to think there's no way to do this in Moq. Thanks for your patience! – mo. Feb 20 '14 at 14:57
  • 1
    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);` – Jesse Sweetland Feb 20 '14 at 15:36