1、自然输入

  自然输入是指对底层函数的正常调用即可获得的内部输入。代码一中Compare()函数内,int a1 = GetArea(r);可以自然取得外接正方形的面积。如果外接正方形面积a1要得到某个预期的值,要传递合适的半径r,半径r称为间接输入。间接输入需根据自然输入及底层函数的功能来倒推,要获得符合预期的自然输入有三个条件:一是底层函数存在,二是底层函数正确,三是间接输入正确。很多时候,自然输入很简单,而间接输入很复杂,这是难于初始化。

  2、不可控

  是指调用实际代码,但实际代码的输出难于控制,无法产生测试需要的指定值。例如,底层函数返回一个随机数,是不可控。在实际项目中,不可控是很常见的,下面的代码是空调控制程序中的一个函数:


extern int GetTemperature(int *pTemperature); //取环境温度
int gExpectTemperature = 25;    //全局变量,预设的目标温度
/*
功能: 空调控制程序片断,取得环境温度并计算制冷器需运行的时间
参数: pWorkTime, 输出参数,保存制冷器需运行的时间
返回: int类型,如果函数执行失败,返回0,否则返回非0值
*/
int WorkTime(int *pWorkTime)
{
      int success = 0;  //取环境温度是否成功
    int temperature;  //环境温度

      //取环境温度,这是测试难点
    success = GetTemperature(&temperature);

      if(!success) return 0;

      //计算温度差,gExpectTemperature是全局变量
      int TempDiff = temperature - gExpectTemperature;
      if(TempDiff <= 0) return 0;

    if(pWorkTime == 0) return 0;          

      //为了简化问题,这里假设温差一度,需运行60秒
    *pWorkTime = TempDiff * 60;
    return 1;
}
 


  测试的难点在于success = GetTemperature(&temperature);,这行代码调用GetTemperature()取环境温度,如果操作成功,success等于1,操作不成功,success等于0;取得的环境温度保存在局部变量int temperature中。假设在实际环境中测试,调用的都是实际代码。首先要设定预期的温度gExpectTemperature,例如设为25,这是全局变量,容易做到。还要测试各种环境温度下程序的行为,例如,至少要测试25,大于25和小于25三种情况,显然,这是很困难的,真实的环境温度在短时间内很难大幅变化,即使大幅变化,也未必符合测试需求,这是不可控。

  3、失真

  失真是打桩造成的,是打桩的必然后果。上面的示例,假如GetTemperature()未实现,或者由于解耦合的目的必须隔离,或者试图解决不可控的问题打桩来代替,桩代码大致是这个样子:


int GetTemperature(int *pTemperature)
{
      return 0;
}


  直接返回0,此外什么也不做。调用GetTemperature()后,success总是为0,环境温度temperature未初始化,测试无法进行。

  如何解决失真?一种思路是修改桩代码,使它实现一些功能,例如,给每个用例起一个名字,桩代码判断当前用例名并做合适的操作。这种方法比较麻烦,并且只能适应简单情形。一个桩可能被同一函数在同一用例多次调用,每次要求输出不同;一个桩可能被多个函数调用;一个函数又可能调用多个桩;一个函数可能需要十几个用例,随时可能补充和修改用例。要维护用例名和桩行为的对应关系,无疑是一场噩梦。