内部输入有以下几种情形:
自然内部输入
这是指对底层函数的正常调用即可获得的内部输入,前面的示例就属于自然输入。代码一中Compare()函数,int a1 = GetArea(r);可以自然取得外接正方形的面积。如果外接矩形面积a1要得到某个预期的值,要传递合适的半径r。自然输入有两个条件:一是底层函数存在,二是底层函数正确。
不可控的内部输入
是指调用实际代码,但实际代码的输出难于控制,难于把各种可能输出都测试到。例如,底层函数返回一个随机数,就是不可控。在实际项目中,不可控是很常见的,下面的代码是空调控制程序中的一个函数(代码清单4.3.cpp):
/*
函数说明:
功能: 空调控制程序片断,取得环境温度并计算制冷器需运行的时间
参数: pWorkTime, 输出参数,保存制冷器需运行的时间
返回: int类型,如果函数执行失败,返回0,否则返回非0值
*/
extern int GetTemperature(int* pTemperature);
int gExpectTemperature = 25;
int WorkTime(int* pWorkTime)
{
int success = 0; //取环境温度是否成功
int temperature; //环境温度
////取环境温度
success = GetTemperature(&temperature);
if(!success)
return 0;
//后面的代码与_03_WorkTime2完全一致
//计算温度差,gExpectTemperature是全局变量
int TempDiff = temperature - gExpectTemperature;
if(TempDiff <= 0)
return 0;
if(pWorkTime == 0)
return 0;
//为了简化问题,这里假设温差一度,需运行一分钟
*pWorkTime = TempDiff * 60;
return 1;
}
代码的重点在于success = GetTemperature(&temperature);,这行代码调用GetTemperature()取环境温度,如果操作成功,success等于1,操作不成功,success等于0;取得的环境温度保存在局部变量int temperature中。假设在实际环境中测试,调用的都是实际代码。我们首先要设定预期的温度gExpectTemperature,例如设为25,这是全局变量,容易做到。我们还要测试各种环境温度下程序的行为,例如,至少要测试25,大于25和小于25三种情况,显然,这是很困难的,真实的环境温度在短时间内很难大幅变化,即使大幅变化,也未必符合测试需求,这就是不可控。
失真的内部输入
失真是打桩造成的,是打桩的必然后果。上面的示例,假如GetTemperature()未实现,或者由于解耦合的目的必须隔离,或者试图解决不可控的问题打桩来代替,桩代码大致是这个样子(代码清单4.4.cpp):
int GetTemperature(int* pTemperature)
{
return 0;
}
直接返回0,此外什么也不做。调用GetTemperature()后,success总是为0,环境温度temperature未初始化,测试无法进行。
一种思路是修改桩代码,使它实现一些功能,例如,给每个用例起一个名字,桩代码判断当前用例名并做合适的操作。这种方法比较麻烦,并且只能适应简单情形。一个桩可能被多个被测函数调用,一个被测函数又可能调用多个桩,要维护用例名与桩行为之间的匹配关系,无疑是一场噩梦。