假设您有一个具有某些任意属性的类:
class Data {
String a = '';
int b = 0;
bool c = false;
SomeObject d = SomeObject();
}
我们也说 somewhere ,您有一个函数想要将此Data
对象属性的大部分但不是全部重置为与以下属性不相关的属性:对象构造函数的默认值。
Data data = Data();
...
void resetData() {
data = data
..a='reset'
..b=42
..c=true;
// We want to retain [d]'s state, for whatever reason.
}
您如何对这种行为进行单元测试?
您可以进行单元测试,将Data
的每个属性设置为与重置默认值完全不同的某种值,并验证所有相关字段都发生了变化,但这是1)令人难以置信的脆弱和2)失败单元测试的目的。如果添加了也应该重置的另一个对象e
,但是却忘记了将其添加到resetData
函数中,则几乎可以肯定也忘记了将其添加到单元测试中。这样,单元测试将无法提供任何价值,因为行为将被破坏,但您不会受到任何警告。
通过测试对象的每个变量确实是不同的(不是dart:mirrors
),可以选择通过d
使用反射/自省,但是dart:mirrors
不适用于AngularDart,因此用户其中的那一块又高又干。
我也找不到任何可以通过为对象填充垃圾值来“模糊”对象的库,因此不能完全确定如何进行处理(或者我是否应该在这个看似愚蠢的单元测试上浪费我的时间)。
答案 0 :(得分:0)
您的问题与该问题的发展方向相似,即是否应该对getter和setter进行单元测试(请参阅Should unit tests be written for getter and setters?)。从某种意义上讲,您的方法resetData
的代码示例比setter更为琐碎,因为为属性分配了常量而不是参数值。
并且,测试各个属性正确设置的单元测试只会从代码中复制该值。使用此类测试发现错误的可能性很小。而且,让第二位开发人员查看测试并不比让第二位开发人员检查代码本身更好-代码审查甚至可以更好地利用开发时间。这些测试可能会像回归测试一样有价值,但是,在大多数情况下,对代码进行更改的情况下,必须保持测试只是为了跟随代码的更改。
因此,与getter和setter以及琐碎的构造函数类似,我建议不要为resetData
编写特定的测试,而是尝试通过以下方式使resetData
成为某些(略大)测试场景的一部分:测试resetData
对后续计算的影响:
// Setup:
Data data = Data(); // Construction
... // some calculation on data (optional)
// Exercise:
resetData()
... // some calculation on data
// Verify:
...
毕竟,从用户的角度来看,应该有一个原因,为什么resetData
为这些属性分配其特定值。这些以用户为中心的方案可以帮助进行有用的测试。 (名称resetData
,顺便说一句,违反了最少惊讶的原则,因为人们会认为它会将值重置为初始值。)
您描述的另一个问题(如果添加了新属性,则单元测试不会告诉您尚未更新resetData
)表明您对单元测试的期望值过高:这种现象不限于此琐碎的功能。如果您添加了一个属性却忘记了更新一些复杂的功能以使用它,并且您保留了测试不变,那么测试也将继续通过。
您可能会想到一些巧妙的技巧,例如,保留并比较当前编写方法时将已知的属性列表与当前的属性列表(使用自省功能获得)进行比较,但这对我来说似乎有些过头了-除非您正在开发安全关键代码,我认为dart
可能不是设计的。