代码之家  ›  专栏  ›  技术社区  ›  mo.

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

  •  9
  • mo.  · 技术社区  · 12 年前

    我有以下测试,包括支持类,但我无法确定如何验证依赖项的调用。

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

    根据Moq的测试输出,执行的调用如下:

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

    我在Moq中看到很多关于匿名参数的问题(例如 this this this ),但找不到与使用匿名类型作为实际类型参数相关的任何内容。

    什么可以放在Verify行中,以便它实际验证内部调用?

    注: 我的例子 IDependency 不返回值(我认为它已经足够复杂了),但对于隐式或显式解决问题的答案,会有额外的奖励 Setup() 以及 Verify() .

    使现代化 杰西的解决方案通过了测试,因为我在制定示例时做出了错误的选择。我应该意识到 IGrouping<out TKey, out TElement> 也是一个 IEnumerable<TElement> 。是否有更普遍的解决方案?

    更新2 我觉得我最初的例子可能过于复杂,不能很好地代表我问题的实际标题。是否有任何解决方案适用于这个更直接、切中要害的示例?

    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);
                }
            }
        }
    }
    
    2 回复  |  直到 9 年前
        1
  •  10
  •   DLeh    8 年前

    接受的答案对我来说不起作用,我认为这是因为测试和所讨论的对象在不同的集合中,所以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;
            };
        }
    }
    

    它们可以这样使用:

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

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

        2
  •  7
  •   mo.    12 年前

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

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

    这同样适用于设置。

    关于中的示例 更新2 ,但它需要了解 DoSomething() 据我所知,这是唯一可行的方法:

    var anonymousType = new {county = "Donegal", town = "Lifford"};
    dependency.Verify(d => d.Execute(anonymousType), Times.Once);
    
    推荐文章