[转载]单元测试的方法论(C#)
今天翻看一本书《Pragmatic Unit Testing C# Edition》(单元测试之道),对工具的使用就不多说了。
但其中谈到的单元测试方法论,很有意思,其实不仅仅是单元测试,其他的黑盒测试方式也可以借鉴其中的方法论。
首先,总的方法论:The Right-BICEP
什么是Right-BICEP,是指6个值得测试的具体“部位”:
- Right - 结果是否正确
- B - Boundary, 是否所有的边界条件都是正确的
- I- Inverse,反向关联检查
- C-Cross,交叉检查
- E-Error Condition,能否强制错误条件发生
- P-Performance,能否满足性能要求?
Right是什么自不必说,其他的检查都有一些原则和细节方法支持:
- 边界条件检查的原则是:CORRECT
- 找边界是最有价值的工作之一,一般Bug就出现在这些边界上
- Conformance : 一致性 - 值是否和预期一致
- 错误的格式,要求传输数字字符串,但是实际传入的是“#%%asdfa&”
- Ordering:顺序性 - 值是否入应该的那样,是有序或者无序的
- 再比如要求的是一个有序的List,但是传入的是无序的List
- Range:区间性- 值是否位于合理的最小值和最大值范围内
- 比如合理性存在问题的值:一个人的年龄为10000岁
- Reference:依赖性 - 代码是否引用了一些不在代码本身控制范围之内的外部资源
- Existence:存在性 - 值是否存在(例如是否null、非0、在一个集合中等)
- 常规的参数检查,比如要把内存保存到文件,但是传入的文件名是null或是String.Empty;
- Cardinaliry:基数性 - 是否由恰好足够的值
- Time:相对或者绝对的时间性 - 所有事情的发生是否有序的、是否在正确的时刻、是否恰好及时。
- 比如事件到达的次序是错误的或和期望的不一样:在未登陆系统前,就尝试打印文档。。。
- Conformance : 一致性 - 值是否和预期一致
- 找边界是最有价值的工作之一,一般Bug就出现在这些边界上
- 检查反向关联:
- 是指用反向的逻辑来验证某一个方法,比如一个平方根函数SquareRoot(),我们可以用这个SquareRoot的平方是否和原值相等的方法来验证:double x = SquareRoot(4.0); Assert.AreEqual(4.0, x*x, 0.0001)
- 使用其他手段来实现交叉检查
- 还是拿平方根函数来举例:double x = MyMath.SquareRoot(4.0); double y=Math.Sqrt(4.0); Assert.AreEqual(x, y, 0.0001);
- 强制产生错误条件
- 真实世界中,错误总是会发生:磁盘会满、网线会断、电子邮件多得像掉进黑洞,而程序会崩溃。
- 应该能够通过强制引发错误来测试代码是如何处理所有这些问题的
- 无效参数是最简单的应用,还应该由更复杂的:
- 内存耗光
- 磁盘用满
- 时钟出问题
- 网络不可用或者有问题
- 系统过载
- 受限的调色板
- 分辨率过高或者过低
- ... (单元测试?? 我们系统测试都没有考虑这么清楚)
- 性能特性
- 单元测试中的性能测试,只能称之为粗糙测试,但是这种粗糙测试能够帮助解决很多要到产品发布的时候才发现的大麻烦
- 不需要多说,看代码就明白了:
[Test]
public void FilterRanges()
{
Timer timer = new Timer();
String url1= "Some url";
//首先,跑轻量级的:
timer.Start();
filter.Check(url1);
timer.End();
Assert.IsTrue(timer.ElapsedTime < 1.0);
//然后,跑大的:
timer.Start();
filter.Check(BigList);
timer.End();
Assert.IsTrue(timer.ElapsedTime < 2.0);
.....
} //大概的思想是,一点一点加压,看到什么时候超过接受的程度。
其实,这些思想一点都不难以总结,但是我们中国人似乎总是不愿意从这些理论出发。
我记得我们公司的单元测试与系统测试的用例都是天马行空,想到哪做到那,不愿意被这些方法论束缚。
唉,也许这些方法论有些教条,看其不够**特色,但是,如果我们都能从这些前人总结的方法论出发,至少95%的测试覆盖率问题就会得到解决。
但是,我们做得到吗?