使用Moq,如何验证对象的所有属性是否已被复制?

时间:2009-09-09 13:52:39

标签: c# .net unit-testing moq

我有一个CopyFrom()方法的接口,它复制另一个对象的所有属性。我有一个测试执行几次VerifyGet()调用,以确保从传递的对象中检索每个属性,例如:

Thing target = new Thing();
IThing source = new Mock<IThing>();

target.CopyFrom(source.Object);
source.VerifyGet(t => t.Foo);
source.VerifyGet(t => t.Bar);

我想要一种迭代IThing属性的方法,并验证每个属性是否自动复制,以便在有人添加属性但忘记复制它时测试将失败。有没有办法通过Moq做到这一点?我试过了;

foreach (var prop in typeof(IThing).GetProperties())
{
    source.VerifyGet(t => prop.Invoke(t, null));
}

但它没有用,因为lambda不代表属性访问器。我认为应该有一种方法可以通过Expression类创建一些东西,但我对LINQ不太熟悉,无法确定应该在那里。

2 个答案:

答案 0 :(得分:4)

我不认为Moq可以做到这一点,但首先你需要质疑你的测试是否相关。你真的希望在这里测试一下吗?

CopyFrom方法读取任何属性是否重要?它绝对可以读取所有属性而无需将它们写入新实例,因此这种基于交互的测试并不能真正证明任何东西。

我猜你真正想测试的是目标的属性是否等于源的属性?

假设IThing上的属性是可写的,您可以使用SetupAllProperties方法创建一个包含所有属性的Stub:

var sourceStub = new Mock<IThing>();
sourceStub.SetupAllProperties();
sourceStub.Object.Bar = "Bar";
sourceStub.Object.Foo = "Foo";

然后,您需要将目标与源进行比较,以查看是否所有属性都匹配。您可以通过在包装真实目标的类中实现特定于测试的Equals方法来实现此目的。

如果您认为这工作太多,您可能需要查看AutoFixture的Likeness类,它会为您提供通用的特定于测试的相等比较。那将允许你继续这样的测试:

var expectedResult = new Likeness<IThing>(sourceStub.Object);

target.CopyFrom(sourceStub.Object);

Assert.AreEqual(expectedResult, target);

Likeness使用Reflection简单地遍历包装对象中的所有公共属性,并查看比较对象是否具有相同的属性值。

答案 1 :(得分:1)

/// <summary>
/// Verifies that a property was read on the mock
/// </summary>
public static void VerifyGet<T>(this Mock<T> mockedObject, string propertyName) where T : class
{
    var property = typeof(T).GetProperty(propertyName);
    if (property == null)
        throw new ArgumentException(string.Format("No property by the name '{0}' was found on '{1}'.", propertyName, typeof(T).Name));

    // getPropFuncExpression = obj => obj.propertyName;        
    var parameterExpression = Expression.Parameter(typeof(T), typeof(T).Name);
    var propertyExpression = Expression.Property(parameterExpression, property);
    var getPropFuncExpression = Expression.Lambda(propertyExpression, parameterExpression);

    var verifyGet = mockedObject.GetType().GetMethods().Single(m => m.Name == "VerifyGet" && m.GetParameters().Length == 1);
    verifyGet.MakeGenericMethod(property.PropertyType).Invoke(mockedObject, new object[] { getPropFuncExpression });
}

您可以添加上述扩展方法,以便只需调用:

foreach (var prop in typeof(IThing).GetProperties())
{
    source.VerifyGet(prop.Name);
}