在单元测试中向对象断言未经验证的添加属性
本文关键字:验证 添加 属性 断言 单元测试 对象 | 更新日期: 2023-09-27 18:03:53
我有MSTest (VS 2012)单元测试,我想断言对象的各种属性具有我想要的值。有很多方法可以做到这一点。然而,我主要担心的是,如果一个新属性被添加到对象中,很容易忽略更新单元测试以确保它具有我们期望的值。
我能想到的一件事是使用反射来枚举对象的公共属性,然后跟踪单元测试断言了哪些属性,最后断言是否有任何属性没有被检查。
有没有人写过类似的东西?
有更好的主意吗?
更新:我应该指出,所讨论的对象类似于数据传输对象,其中有其他类/方法导致该对象中的数据被更新。我们很容易忽略为那些类/方法更新测试以确保我们考虑到所有对象的属性。我想要一些更强大的东西(即不能被遗忘或忽视),而不是右键单击对象,查找引用,并检查代码。
例如: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.
}
谢谢,丹
你们的要求并非不合理。对于简单的Domain类到DTO的映射,可以使用Automapper这样的框架。你只要告诉它域类A映射到dto,它就会使用约定而不是配置来映射属性。该框架还附带了一种方法来测试您是否已经完成了所有映射。您可以调用Mapper.AssertConfigurationIsValid();
,它将确保Dto中的每个属性都映射到域类中的属性(代码中的显式或具有相同属性名称的隐式)。
所以基本上你在尝试做同样的事情,这就是为什么它是一个非常有效的场景。假设你选择不使用Automapper。我猜你的代码中没有单独的mapper
(在这种情况下,你可以使用反射来测试你的映射器类/代码)。我做过类似的事情,所有的dto实现一个DeepCopy方法,返回另一个IDto。但我们希望实际类型与调用类型
[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 object的所有公共属性并遍历它们,并断言您返回的具体person对象包含该特定属性的非空值。
尽管在这种情况下,测试必须仅用于非常特定的对象,您知道所有属性必须具有非空值。例如,对于值类型,如果AccountBalance属性返回0,那么这个人的余额是0还是没有设置该属性?如果这个人的email属性是空的,这是否意味着它没有被映射或者我们只是没有这个人的email在系统中。这就是为什么如果你有特定的映射器类或使用Automapper,那么你更具体地测试映射是否存在,而不是"这是否有一个值",正如我所说的,这可能仍然适用于你的应用程序中的一些有限的类。
另一个选择是装饰属性,你知道不能为空,必须与自定义属性映射。例如
Public class PersonDto {
[TestNotNull]
public string FirstName { get; set}
}
然后在你的单元测试中,你可以使用反射来找到所有带有这个属性的属性,并确保它们不是空的。当然,你会遇到同样的问题,你试图在不同的层面上解决。如果您担心人们会忘记映射重要的字段,那么就不能保证他们会记得用自定义属性来修饰这些字段。
当然,您可能不想测试属性是否被映射,而只想测试属性是否存在。在这种情况下,使用反射来获得域对象类型的所有属性列表,并确保它们中的每一个都存在于DTO对象类型的属性列表中。但是这并没有测试属性本身是否被映射,而只是测试它们是否存在。如果您使用Automapper和它的AssertConfigurationIsValid()
测试,您可以测试DTO中的每个属性是否被正确映射,而不是域中的每个属性在DTO中都有一个相关的属性。
所以要回答你的问题,如果你想测试属性是否存在并且它是否被正确映射,你需要两个测试。一个是比较域对象类型和Dto对象类型,并确保其中一个对象的属性存在于另一个对象中,另一个是确保Dto中的所有属性都被映射到某些东西(无论您是在自己的代码中进行映射还是使用Automapper之类的框架)。