TDD与VTDD系列(二):CppUnit的改进与使用(2)

4 测试过程


示例类定义如下:


class CMyClass
{
public:
    //加法函数
    int Add(int a, int b);

    //计算空调制冷器运行时间
    int WorkTime(int* pSecond);
};


加法函数Add()是入门示例,WorkTime()是接近应用的示例,功能是计算空调制冷器运行时间,需调用桩代码取得环境温度。测试过程,使用VC6.0。


假设CppUnit安装目录为D:/CppUnit/CppUnit,被测试文件MyClass.cpp和MyClass.h位于D:/CppUnit/Project目录。


1、用VC6.0建立一个一程,即测试工程。第一步左边选择MFC AppWizard(exe),工程名为TestProject,存放位置与Project并列,即存放于D:/CppUnit/TestProject。第二步选择Dialog based,点击Finish。这个示例使用了图形界面,所以要建立一个对话框工程。


2、把文件CppUnitPub.h和TestRun.cpp拷贝到D:/CppUnit/TestProject。


3、拷贝TestClassName.cpp和TestClassName.h文件到D:/CppUnit/TestProject,并重命名文件,将文件名中ClassName替换为实际类名。这里,被测试类名是CMyClass,因此,把文件名改为TestMyClass.cpp/.h。


用文本编辑工具如EditPlus打开这两个文件,进行以下替换:
TestClassName替换为TestMyClass。
ClassName替换为CMyClass。
TestHeaderName替换为TestMyClass.h。
HeaderName替换为MyClass.h。


4、将2、3拷贝和修过的文件、以及被测试类的文件(即D:/CppUnit/Project目录下的MyClass.cpp/.h)加入测试工程,方法是在VC6.0的Project菜单,单击Add files…。


5、设置头文件搜索目录,包括CppUnit,以及产品项目的头文件目录:VC6.0->Project->Setting->C++,Category选择Preprocessor,在Additional include directories中添加搜索目录:D:/CPPUNIT/CPPUNIT/include,D:/CPPUNIT/Project。


6、加入CppUnit的静态和动态库。VC6.0->Project->Setting->Link,Category选择Input,Application library path填写D:/CPPUNIT/CPPUNIT/lib。将CppUnit目录下的TestRunner.dll文件拷贝到D:/CppUnit/TestProject。


7、被测试函数WorkTime()调用了一个未实现的函数,要用桩来代替,后面再介绍桩代码编写,先把其中的代码行success = GetTemperature(&temperature);前加上注释符//。如果设定过程没有错误,这时应该可以通过编译。


8、在CTestProjectApp::InitInstance()函中的开头添加以下代码:
void TestRunAll();
TestRunAll();
return FALSE;


9、在TestRun.cpp文件的TestRunAll()函数中加入测试类:runner.addTest(TestMyClass::GetSuite());


10、编写CMyClass::Add()函数的测试函数,在TestMyClass.cpp文件中添加如下代码:
void TestCMyClass::testAdd()
{
     CASE_BEGIN("");
     int a = 1;
     int b = 1;
     int ret = pObj->Add(a, b);
     CPPUNIT_ASSERT_EQUAL(2, ret);
     CASE_END();

 

     CASE_BEGIN("");
     int a = 10;
     int b = 10;
     int ret = pObj->Add(a, b);
     CPPUNIT_ASSERT_EQUAL(20, ret);
     CASE_END();
}


在TestMyClass.h文件中添加测试函数声明void testAdd();并在CPPUNIT_TEST_SUITE宏内添加注册代码CPPUNIT_TEST(testAdd);


编译并运行后会显示CppUnit的图形界面,点击“Browse”,选择要执行的测试,再点击“Run”,即会显示测试结果。由于CMyClass::Add()函数中把+写成了-,所以测试会失败,修改后测试通过。


11、编写CMyClass::WorkTime()函数的测试函数,先把7所添加的注释符删除。这个函数首先调用int GetTemperature(int* pTemperature)函数取得环境温度,然后根据环境温度和预设的目标温度的差,计算制冷器的运行时间。在TestMyClass.cpp添加测试函数:


extern int gExpectTemperature;
void TestMyClass::testWorkTime()
{
    CASE_BEGIN("failed");
    gExpectTemperature = 25;
    int second = 0;

    int ret = pObj->WorkTime(&second); 
    CPPUNIT_ASSERT_EQUAL(0, ret); 
    CPPUNIT_ASSERT_EQUAL(0, second); 
    CASE_END();

 

    CASE_BEGIN("ok-23");
    gExpectTemperature = 25;
    int second = 0;
    int ret = pObj->WorkTime(&second);
    CPPUNIT_ASSERT_EQUAL(0, ret);
    CPPUNIT_ASSERT_EQUAL(0, second);
    CASE_END();

 

    CASE_BEGIN("ok-28");
    gExpectTemperature = 25;
    int second = 0;
    int ret = pObj->WorkTime(&second);
    CPPUNIT_ASSERT_EQUAL(1, ret);
    CPPUNIT_ASSERT_EQUAL(180, second);
    CASE_END();

 

    CASE_BEGIN("ok-25");
    gExpectTemperature = 25;
    int second = 0;
    int ret = pObj->WorkTime(&second);
    CPPUNIT_ASSERT_EQUAL(0, ret);
    CPPUNIT_ASSERT_EQUAL(0, second);
    CASE_END();
}


在TestMyClass.h文件中添加测试函数声明void testWorkTime ();并在CPPUNIT_TEST_SUITE宏内添加注册代码CPPUNIT_TEST(testWorkTime);


现在编译还无法通过,因为GetTemperature()未实现。假设我们对GetTemperature()函数的测试需求如下表,要使用例与桩输出匹配,如何编写桩代码呢?可以用命名法来匹配用例与桩输出。

 

用例

返回值

出参(环境温度)

1

0(取环境温度失败)

 

2

1(取环境温度成功)

23

3

1

25

4

1

28

  
在测试代码中,每个用例的第一行可以给用例命名,如CASE_BEGIN("failed"),表示取环境温度失败,CASE_BEGIN("ok-23"),表示取环境温度成功,值为23。


在测试工程目录下新建文件Stub.cpp,并加入测试工程,编写桩代码,如下:
#include "stdafx.h"
#include "CppUnitPub.h"

int GetTemperature(int* pTemperature)
{
    if(caseNameIs("failed"))
        return 0;

 

    if(caseNameIs("ok-23"))
    {
        *pTemperature = 23;
        return 1;
    }

 

    if(caseNameIs("ok-25"))
    {
        *pTemperature = 25;
        return 1;
    }

 

    if(caseNameIs("ok-28"))
    {
        *pTemperature = 28;
        return 1;
    }

 

    return 0;
}


桩代码调用caseNameIs()来判断当前用例名,并输出合适的值。


本例中,如果GetTemperature()调用的是实际代码,在测试时称为不可控,即无法控制真实的环境温度使其满足测试的需要;如果调用的是桩代码,且桩代码只是空函数,则称为失真,简单的失真可以用命名法解决,但不能解决复杂的失真,例如,GetTemperature()在同一用例中被多次调用,每次要求输出不同;或者桩与被测函数的关系是多对多,这就难于维护桩输出与用例的关系了。另外,编写桩代码也不能解决不可控,假如GetTemperature()也是CMyClass类的成员函数,在不修改产品代码的前提下,无法调用桩。这些问题,就不是开源框架所能解决的了。

你可能感兴趣的:(TDD,测试,application,dialog,文本编辑,preprocessor)