5

I have MSTest (VS 2012) unit tests where I want to assert that the various properties of an object have the values I want. There are many ways to do this. However my main concern is that if a new property is added to the object, it's easy to overlook updating the unit tests to make sure that it has the values we expect.

One thing I can think of is to use reflection to enumerate the public properties of the object, then keep track of which properties the unit test has asserted, and at the end, assert if any properties did not get checked.

Has anybody already written something similar?

Any better ideas?

Update: I should point out that the objedct in question is something like a Data Transfer Object where there are other classes/methods that cause data in that object to be updated. It's easy to overlook updating the tests for those classes/methods to make sure that we account for all of the object's properties. I want something a bit stronger (i.e. can't be forgotten or overlooked) than right-clicking on the object, finding references, and reviewing the code.

For example:

public class Person {
  public string FirstName;
}

public Person GetPerson() {}

[TestMethod]
public void GetPerson_ReturnsFilledInPerson()
{
    var actual = target.GetPerson();
    Assert.IsNotNull(actual.FirstName);
    // If somebody later adds LastName to Person, 
    // we want this unit test to fail until the LastName is checked too.
}

Thanks,

Dan

4

1 回答 1

1

你所要求的并不是没有道理的。对于简单的 Domain 类到 DTO 的映射,您可以使用 Automapper 等框架。您只需告诉它域类 A 映射到 dto ADto,它将使用约定优于配置来映射属性。该框架还附带了一种测试您是否已完成所有映射的方法。您可以调用Mapper.AssertConfigurationIsValid();,它将确保 Dto 中的每个属性都已映射到域类中的属性(在代码中显式或通过具有相同的属性名称而隐式映射)。

所以基本上你正在尝试做同样的事情,这就是为什么它是一个非常有效的场景。假设您选择不使用 Automapper。我猜您的代码中没有单独mapper的代码(在这种情况下,您可以使用反射来测试您的映射器类/代码)。我做过类似的事情,所有 DTO 都实现了返回另一个 IDto 的 DeepCopy 方法。但我们希望实际类型与调用类型相同

    [Test]
    public void DeepCopyReturnsSameTypeAsOriginal()
    {
        var iDtoType = typeof (IDto);
        var allIDtoTypes = iDtoType.Assembly.GetTypes().Where(t => iDtoType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList();

        foreach (var currentIDtoType in allIDtoTypes)
        {
            var instanceOfDtoType = (IDto)Activator.CreateInstance(currentIDtoType);
            var deepCopyType = instanceOfDtoType.DeepCopy();
            Assert.AreEqual(instanceOfDtoType.GetType(), deepCopyType.GetType(), 
                                string.Format("Deep Copy of Type '{0}' does not return the same Type. It returns '{1}'. The DeepCopy method of IDto should return the same type.",
                                instanceOfDtoType.GetType(), deepCopyType.GetType()));
        }
    }

您可以使用类似的逻辑来获取您的人员对象。获取人员对象的所有公共属性并对其进行迭代并断言您返回的具体人员对象包含该特定属性的非空值。

尽管在这种情况下,测试只能用于非常特定的对象,您知道所有属性都必须具有非空值。以值类型为例,如果 AccountBalance 属性返回 0,那么此人的余额是 0 还是未设置该属性?如果此人的电子邮件属性为空,这是否意味着它未映射,或者我们只是在系统中没有此人的电子邮件。这就是为什么如果您有特定的映射器类或使用 Automapper,那么您将更具体地测试映射是否存在,而不是“这是否具有价值”,正如我所说,这可能仍然适用于您的应用程序中的一些有限类。

另一种选择是装饰您知道不能为空且必须使用自定义属性映射的属性。例如

Public class PersonDto {
    [TestNotNull]
    public string FirstName { get; set}

}

然后在您的单元测试中,您可以使用反射来查找具有此属性的所有属性并确保它们不为空。但是,当然,您会遇到您试图在不同级别解决的相同问题。如果您担心人们会忘记映射重要字段,则不能保证他们会记得使用自定义属性来装饰这些字段。

当然,您可能不想测试该属性是否正在被映射,而只想测试该属性是否存在。在这种情况下,使用反射来获取域对象类型的所有属性的列表,并确保它们中的每一个都存在于 DTO 对象类型的属性列表中。但这并不能测试属性本身是否被映射,只测试它们是否存在。如果您使用 Automapper 及其AssertConfigurationIsValid()测试,您测试的是 DTO 中的每个属性都被正确映射,而不是域中的每个属性在 DTO 中都有关联的属性。

因此,要回答您的问题,如果您想测试该属性是否存在以及是否正确映射,您需要两个测试。一个是比较域对象类型和 Dto 对象类型,并确保一个对象的属性存在于另一个对象中,另一个测试是确保 DTO 中的所有属性都被映射到某个东西(不管你是否正在做映射在您自己的代码中或使用 Automapper 之类的框架)。

于 2013-03-21T23:48:55.583 回答