Oha*_*der 5 language-agnostic unit-testing
考虑以下课程:
public class MyIntSet
{
private List<int> _list = new List<int>();
public void Add(int num)
{
if (!_list.Contains(num))
_list.Add(num);
}
public bool Contains(int num)
{
return _list.Contains(num);
}
}
Run Code Online (Sandbox Code Playgroud)
遵循"仅测试一件事"原则,假设我想测试"添加"功能.考虑以下这种测试的可能性:
[TestClass]
public class MyIntSetTests
{
[TestMethod]
public void Add_AddOneNumber_SetContainsAddedNumber()
{
MyIntSet set = new MyIntSet();
int num = 0;
set.Add(num);
Assert.IsTrue(set.Contains(num));
}
}
Run Code Online (Sandbox Code Playgroud)
我对这个解决方案的问题是它实际上测试了两个方法:Add()和Contains().从理论上讲,两者都可能存在错误,只会出现在一个接一个不被调用的情况下.当然,Contains()现在服务器作为List的Contains()的瘦包装器,它本身不应该进行测试,但如果它将来会变得更复杂呢?也许应该始终保留一个简单的"薄包裹"方法用于测试目的?
另一种方法可能建议模拟或暴露(可能使用InternalsVisibleTo或PrivateObject)私有_list成员并让测试直接检查它,但如果有一天内部列表被其他一些集合替换(可能是C5),那么可能会产生测试可维护性问题).
有一个更好的方法吗?我反对上述实现的任何论据都有缺陷吗?
在此先感谢,JC
你的测试对我来说似乎完全没问题.您可能误解了单元测试的原则.
单个测试应该(理想情况下)只测试一件事,这是真的,但这并不意味着它应该只测试一种方法; 相反,它应该只测试一种行为(不变量,遵守某个业务规则等).
您的测试测试行为"如果您添加到新集合,它不再是空的",这是一个单一的行为:-).
解决你的其他问题:
从理论上讲,两者都可能存在错误,只会出现在一个接一个不被调用的情况下.
没错,但这只是意味着你需要更多测试:-).例如,添加两个数字,然后调用Contains,或调用Contains而不添加.
另一种方法可能建议模拟或暴露(可能使用InternalsVisibleTo)私有_list成员并让测试直接检查它,但这可能会产生测试可维护性问题[...]
非常正确,所以不要这样做.单元测试应始终针对被测设备的公共接口.这就是为什么它被称为单元测试,而不是"在一个单元内乱搞"-test ;-).