单元测试思想之如何编写测试代码

上一篇 / 下一篇  2006-12-14 11:43:07

  使用VU时,测试代码是自动生成的,但了解手工编写测试代码的方法,有助于更好地实施单元测试。这里用一个简单的例子,说明如何编写测试代码进行单元测试。可以用一个简单的方式来组织测试代码:一个类对应一个测试类,一个函数对应一个测试函数。

产品类:
class CMyClass 
{
public:
   intAdd(inti,intj);
    CMyClass();
   virtual~CMyClass();

private:
   intmAge; //年龄
    CString mPhase; //年龄阶段,如"少年","青年"
};

建立对应的测试类CMyClassTester,为了节约篇幅,只列出源文件的代码:
voidCMyClassTester::CaseBegin()
{
   //pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,
   //为求简单,所有的测试类都可以用pObj命名被测试对象的指针。
    pObj =newCMyClass();
}

voidCMyClassTester::CaseEnd()
{
   deletepObj;
}
测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。

接下来,我们建立示例的产品函数:
intCMyClass::Add(inti,intj)
{
   returni+j;
}
和对应的测试函数:
void CMyClassTester::Add_int_int()
{
}
把参数表作为函数名的一部分,这样当出现重载的被测试函数时,测试函数不会产生命名冲突。下面添加测试用例:
voidCMyClassTester::Add_int_int()
{
   //第一个测试用例
    CaseBegin();{//1
   inti = 0;   //2
   intj = 0;   //3
   intret = pObj->Add(i, j);//4
    TEST_ASSERT(ret == 0);    //5
    }CaseEnd();               //6
}
  第1和第6行建立和销毁被测试对象,所加的{}是为了让每个测试用例的代码有一个独立的域,以便在多个测试用例中使用相同的变量名。
  第2和第3行是定义输入数据,第4行是调用被测试函数,这些容易理解,不作进一步解释。第5行是预期输出,它的功能是当实际输出与预期输出不同时自动报错,TEST_ASSERT(exp)是一个断言宏,表示当exp的计算结果为真时,测试通过,否则,输出报告错误的信息。
  示例中的格式显得很不简洁,2、3、4、5行可以合写为一行:TEST_ASSERT(pObj->Add(0, 0) == 0);但这种不简洁的格式却有很大的优点,因为它一目了然,易于建立多个测试用例,并且具有很好的适应性,同时,也是极佳的代码文档。
  建立了第一个测试用例后,最好编译并运行测试,以排除语法错误,然后,使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小,通常只需修改一两个数据,拷贝/修改是建立多个测试用例的最快捷办法。
  
上面是一个最简单的例子,下面再举一个涉及成员变量的例子,这是产品函数:
voidCMyClass::Grow(intyears)
{
    mAge += years;

   if(mAge < 10)
        mPhase = "儿童";
   elseif(mAge <20)
        mPhase = "少年";
   elseif(mAge <45)
        mPhase = "青年";
   elseif(mAge <60)
        mPhase = "中年";
   else
        mPhase = "老年";
}

    这是测试函数中的一个测试用例:
    CaseBegin();{
   intyears = 1;
    pObj->mAge = 8;
    pObj->Grow(years);
    TEST_ASSERT( pObj->mAge == 9 );
    TEST_ASSERT( pObj->mPhase == "儿童" );
    }CaseEnd();

  前面说过,如果以函数作为测试单元,成员变量的初始值可以看作是输入数据的一部分,其结果值是输出数据的一部分。示例中,首先设定成员变量mAge的初始值,运行被测试函数,在预期输出中,断言成员变量mAge的结果值。如果需要,运行被测试函数前还可以调用其他成员函数,例如:执行被测试函数前可能需要读取文件中的数据保存到成员变量,或需要连接数据库,这些操作称为前置操作。
  为了访问私有成员,可以将测试类定义为产品类的友元类。例如,定义一个宏:
  #define UNIT_TEST(cls) friend class cls##Tester;
  然后在产品类声明中加一行代码:UNIT_TEST(ClassName)。

  实际上,测试代码中还可能涉及到其他数据,例如全局变量、文件中的数据,或数据库中的数据,后文会作进一步的论述。

TAG:

引用 删除 sunnee   /   2008-04-25 13:22:01
1
您真负责~ 谢@
 

评分:0

我来说两句

Open Toolbar