3.3测试代码
下面介绍测试代码的编写。虽然现在比较自动化的工具不需要手工去编写比较多的代码,但是了解测试代码的基本构成是有一些好处的。这里用的例子是C++代码,因为单元测试面对的是源代码,所以不同的语言稍有一些差别,但是基本的原理还是差不多的。
示例产品代码:
v //MyClass.cpp
class CMyClass {
v public:
v int Add(int i, int j)
v {
return i+j;
};
v void Grow(int years)
{
mAge += years;
//其他代码
};
v
v CMyClass();
v virtual ~CMyClass();
v private:
v int mAge; //年龄
v CString mPhase; //年龄阶段
v };
这个代码有两个函数,一个是简单的加法函数,另一个是涉及到成员变量的函数。我们可以这样来组织测试代码:对于C语言,可以每一个产品文件对应建立一个测试文件。对于C++,可以每一个类对应建立一个测试类。每一个产品函数需要测试的话建立一个对应的测试函数。这样比较简单,也容易维护。
测试类:
v class CMyClassTester
v {
v CMyClass* pObj; //被测试类的对象指针
v void CaseBegin(); //用例初始化
v void CaseEnd(); //用例结束
v void ClassTest(); //执行本类中的所有测试函数
v //各个测试函数加到此后
v void Test_Add_int_int();
v void Test_Grow_int();
v };
测试函数:
v void CMyClassTester::Add_int_int()
v {
v //第一个测试用例
v {CaseBegin(); //1
v int i = 0; //2
v int j = 0; //3
v int ret = pObj->Add(i, j); //4
v TestAssert(ret == 0); //5
v CaseEnd(); } //6
v }
这个测试函数只有一个测试用例。这个测试用例的结构是非常清晰简单的。首先是一个初始化的语句,最后是一个清理的语句,中间是一个被调用函数的语句。这三个语句在不同的用例中都是很相似的。int i = 0; int j = 0; 为输入区域,TestAssert(ret == 0);为预期输出区域。一个用例最主要的是它的输入和输出。我们在这个用例的前面和后面加入一个大括号,目的是让这个用例自成一个域,不同的用例可以使用相同的变量名,方便我们建立更多的用例。
使用拷贝和修改可以建立更多的用例,以下测试函数的第二个用例跟第一个用例相比只是输入和输出不同,其他都是完全一样的。拷贝过来修改一下输入输出就是新的用例了。
v void CMyClassTester::Add_int_int()
v {
v {CaseBegin(); //1
v int i = 0; //2
v int j = 0; //3
v int ret = pObj->Add(i, j); //4
v TestAssert(ret == 0); //5
v CaseEnd(); } //6
{CaseBegin(); //1
v int i = 10; //2
v int j = 10; //3
v int ret = pObj->Add(i, j); //4
v TestAssert(ret == 20); //5
v CaseEnd(); } //6
v }
这种用例格式在被测试的输入输出非常简单的时候看起来非常的繁琐,但是这种结构是可以适应各种各样的非常复杂的输入和输出。
下面是一个涉及到成员变量的输入输出的例子,它在用例开头的时候为一个成员变量设定了初始值,后面为两个成员变量判断结果值。
v void CMyClassTester::Grow_int
v {
v {CaseBegin();
v int years = 1;
v pObj->mAge = 8;
v pObj->Grow(years);
v TestAssert( pObj->mAge == 9 );
v TestAssert( pObj->mPhase == "儿童" );
v CaseEnd(); }
v }