2013-03-14 66 views
3

我有MSTest(VS 2012)单元测试,我想断言对象的各种属性具有我想要的值。有很多方法可以做到这一点。但是我主要担心的是,如果将新属性添加到对象中,则很容易忽略更新单元测试以确保它具有我们期望的值。在单元测试中声明未验证的添加属性到对象

我能想到的一件事是使用反射来枚举对象的公共属性,然后跟踪单元测试已声明的属性,最后断言是否有任何属性未被检查。

有人已经写过类似的东西吗?

有什么更好的点子?

更新: 我应该指出的是,有问题的objedct是像数据传输对象那里有其他类/在该对象会对数据进行更新的方法。很容易忽视更新这些类/方法的测试,以确保我们考虑到所有对象的属性。我想要比右键点击对象,查找引用和查看代码更强一些的东西(即不能被遗忘或忽略)。

例如:

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

感谢,

+3

理想情况下,您应该尝试练习TDD,以便在更新代码之前更新测试 – levelnis 2013-03-14 21:00:45

+2

对我来说,* *测试会过多地断言。如果我想确保没有新的属性添加到课堂,我会有一个单独的测试。你也可以看代码覆盖率...... – 2013-03-14 21:03:17

回答

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

您可以使用类似的逻辑来获取您的人员对象。获取person对象的所有公共属性并遍历它们,并声明返回的具体person对象包含该特定属性的非null值。

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

另一个选择是装饰性,你知道,不能为空,必须用自定义属性映射。例如

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

} 

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

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

所以要回答你的问题,如果你想测试这个属性是否存在并且它被正确映射,你需要两个测试。一种是将域对象类型与Dto对象类型进行比较,并确保其中一个的属性存在于另一个中,另一个测试用于确保DTO中的所有属性都映射到某个对象(无论您是否在进行映射在你自己的代码中或使用像Automapper这样的框架)。