最近碰到一个非常有趣的问题,一个函数有一个静态变量,它默认初始化false,满足一定条件时设置为true。代码类似于:
- void powerup()
- {
- static bool a = false;
- …
- if(some condition)
- {
- a = true;
- }
- …
- if(a)
- {
- …
- }
- …
- }
复制代码嵌入式代码中我们可能经常写这样的代码,检测某个启动条件是否满足,根据条件成立与否,做不同的操作。启动代码比较特殊,因为它在程序的生命周期中,只会执行一次,重启后一切恢复如初。这样的代码,程序本身并没有问题,但是它的测试却成了一个问题。 很多UT工具在组织Test Suit和Test Case的时候,会编译成一个可执行程序,这样这个静态变量对整个exe来说都是全局的,前面的Case如果已经将其设置为true,后面的测试Case不能再测试其为false的情况了。我们需要考虑一个问题,我们写的代码只是去完成应有的逻辑,是否应该考虑代码是不是可测的呢? 答案是肯定的。如果把测试推倒UT之后,我们是否会记得一个警告:代码的缺陷越晚发现,代价越高!UT是开发保证质量的重要一关,成本也最低,我们不应该放弃这个最好的防守机会! Agile提倡的一个实践,TDD可以保证我们的代码是可测的。首先来设计测试,我们从稍微高的抽象级别来考虑我们的设计,避免过度沉迷于详细的实现上,细节上我们容易犯错。TDD会先考虑接口,考虑代码的外部表现,这样在实现的时候就会去满足已经定义好的接口,可以发现接口的副作用,及时消除它们。
回到前面提到的问题,现在我们如何来完成上面的代码的测试呢? 1. 我们可以添加一个空函数调用,UT Case里面对其打桩,改变静态变量的值。 2. 分割测试套件,让冲突的测试用例位于不同的exe中。 3. 想办法找到静态变量的地址,做一个强制修正。 这只是一个例子,其实我们可能写过很多代码,都没有办法去测试,或者测试的代价非常巨大。比如直接在应用逻辑里面用SQL访问数据库。如果你需要UT,需要质量,考虑一下代码的可测性吧。
|