C# 使用匿名类型参数进行Moq验证

C# 使用匿名类型参数进行Moq验证,c#,generics,moq,type-parameter,C#,Generics,Moq,Type Parameter,我有下面的测试,带有支持类,但我不知道如何验证对依赖项的调用 [TestFixture] public class AnonymousGenericTypeParameterTests { [Test] public void Test() { // Arrange var dependency = new Mock<IDependency>(); var towns = new List<Town>

我有下面的测试,带有支持类,但我不知道如何验证对依赖项的调用

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

因为类型在测试上下文中是已知的,所以可以为Verify调用提供特定的类型参数。以下更改使测试通过:

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

被接受的答案对我不起作用,我相信这是因为测试和相关对象位于不同的程序集中,所以Moq不知道如何协调类型,并且不匹配它们

相反,我创建了以下帮助器方法,可以验证提供的匿名类型是否具有正确的字段和值:

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

这将创建一个匹配器,该匹配器将使用反射根据匿名类型的键和值来匹配匿名类型,然后您可以使用普通验证来查看它何时被调用。

正在尝试找出这是如何传递的
Execute()
的调用方式如下:
dependency.Execute(country,c=>c.Population)。当我将鼠标移到那里的
country
上时,我看到它的类型是
IGroupingDoSomething将IEnumerable分组以生成IEnumerable。DoSomething然后迭代该IEnumerable并调用IDependency.Execute对每个iGroup元素执行。TKey似乎不重要--country.Key从未被引用,因此匿名对象除了作为分组键外,从未发挥作用。Execute期望调用方提供T,在本例中,调用方总是通过DoSomething传递T。是否还有其他需要支持的DoSomething变体?您为我提供了解决上述示例的见解,并解决了我在生产代码中遇到的原始问题,因此非常感谢!然而,我觉得这是解决原始问题的一个办法,因为我的匿名类型(
I将以下过程分组,但它需要了解DoSomething方法的内部工作原理,据我所知,这是唯一可行的方法:
var anonymousType=new{country=“Donegal”,town=“Lifford”};dependency.Verify(d=>d.Execute(匿名类型),Times.Once);
dependency.Verify(d =>
  d.Execute(It.IsAny<IEnumerable<Town>>(), It.IsAny<Func<Town, decimal?>>()));
var anonymousType = new {county = "Donegal", town = "Lifford"};
dependency.Verify(d => d.Execute(anonymousType), Times.Once);
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;
        };
    }
}
var anon = new { SomeKey = "some value", SomeOtherKey = 123 };
myMock.Setup(x => x.MyMethod(personIDs, AnonHelpers.MatchAnonymousType(anon))).Verifiable();