eVC++ 应用程序的单元测试框架






4.63/5 (12投票s)
eVC++ 应用程序的单元测试框架及其使用。
引言
首先,如果我的英语不好让您感到不适,请原谅。几个月前,我了解到 CPP Unit,这是一个 C++ 应用程序的单元测试框架。我去年刚大学毕业,对测试框架不太了解,所以使用 CPP Unit 对我来说是一次非常好的体验。由于我还在使用 Windows CE 进行编程,因此也有必要为那些应用程序编写测试用例。但是 CPP Unit 的问题在于,我无法为 Windows CE 编译它,因为它使用了一些 eVC++ 编译器不支持的 C++ 特性。我尝试寻找一个类似的 Windows CE 测试框架,但没有找到。因此,经过长时间的搜索,我决定为 Windows CE 应用程序编写自己的单元测试框架。这就是我最终的成果,我想与大家分享。
这是一个特别为 Windows CE 应用程序编写的单元测试框架。它提供了图形界面来选择和运行测试用例,并显示结果。如果用户需要,它还可以将运行的测试结果保存到文件中。
背景
在 WinCE 上使用 C++ 编程非常受限。它不支持模板、RTTI 和 C++ 的其他功能。CPP Unit 使用模板和 RTTI,但在 eVC++ 中不可用。这就是为什么我使用了大量的成员函数指针和回调来实现它。
使用框架
编译 WCEUnit 项目以生成特定平台的 WCEUnit.dll 文件。为了使用测试框架,测试用例项目必须与 WCEUnit.dll 链接。编写您的测试用例并将项目链接到 WCEUnit.dll。以下主题将讨论如何编写测试用例。
编写测试用例
当您编写测试类时,它应该从框架中定义的 CTestSuite 类派生。CTestSuite 类包含关于为该特定测试套件编写的测试用例的所有信息。应将单独的测试用例添加到测试套件中,如下例所示。下面示例中使用的宏定义在 WCEMacros.h 头文件中。这些宏封装了测试用例的添加机制。下面的代码显示了一个用于测试 CComplex 类的测试套件类。CComplex 类非常简单,实现了复杂的数字的基本运算,我将不详细介绍。
//TestComplex.h
#ifndef __TEST_COMPLEX_INCLUDED__
#define __TEST_COMPLEX_INCLUDED__
#include "..\WCEUnit\TestSuite.h"
#include "..\WCEUnit\WCEUnitMacros.h"
#include "Complex.h"
class CTestComplex : public CTestSuite
{
public:
    //Pass the name of the test class as parameter.
    WCEUNIT_TESTSUITE_INIT(CTestComplex);
    //Pass the name of the test case function as parameter.
    WCEUNIT_ADD_TESTCASE(testAdd);
    WCEUNIT_ADD_TESTCASE(testEqual);
    WCEUNIT_ADD_TESTCASE_EXCEPTION(testExcp, CUserException);
    WCEUNIT_TESTSUITE_END();
public:
    /***********************************************************
    The signature of test case function should be void (func)();
    ***********************************************************/
    void testAdd();
    void testEqual();
    void testException();
    void CleanUpTest();
    void InitializeTest(); 
private:
    CComplex*        m_p1;
    Ccomples*        m_p2;
    CComplex*        m_p3;
};
#endif //__TEST_COMPLEX_INCLUDED__
宏说明
- WCEUNIT_TESTSUITE_INIT(testSuite)- 将测试套件类名传递给它。此函数定义了一个 - CreateTestSuite()函数的开始。此函数在- CTestSuite类中定义为纯虚函数,用于创建测试用例列表。
- WCEUNIT_ADD_TESTCASE(testCase)- 此宏将代码添加到 - CreateTestSuite()函数中,以添加一个测试用例函数。
- WCEUNIT_ADD_TESTCASE_EXCEPTION(testCase, testException)- 此宏用于添加一种特殊类型的测试用例函数,该函数应该针对特定类型的异常进行测试。如果测试用例未抛出异常,则认为测试失败。 
- WCEUNIT_TESTSUITE_END()- 此宏结束 - CreteTestSuite()函数。
您可以在类的定义中看到 InitializeTest() 和 CleanUpTest() 函数。这些函数在每次测试用例执行之前和之后被调用。从 UI 中,您可以选择这些函数如何被调用,即您希望这些函数为每个测试用例调用,还是只为某个测试套件类的所有测试用例调用一次。您可以通过在主对话框上选中或取消选中 **Init Once** 复选框来指定您的选择。
编写测试条件
在编写测试用例时,请使用 WCEUnitMacros.h 中定义的宏来编写测试条件。以下是用于编写测试条件的宏:
- WCEUNIT_ASSERT(condition)- 如果 - condition的计算结果为 false,则断言并标记测试为失败。
- WCEUNIT_ASSERT_FAIL(condition)- 这是上面宏的反向。如果您希望条件必须返回 false,请使用此宏来测试条件。 
- WCEUNIT_ASSERT_EQUALS(testValue, compValue)- 如果 - testValue和- compValue不相等,则此宏断言。如果两个值不相等,则标记测试用例为失败。如果您正在使用用户定义的类对象进行比较,则该类必须重载 '- ==' 运算符。
- WCEUNIT_DOUBLE_EQUALS(testValue, compValue)- 此宏与上面一个类似,但它假定 - testValue和- compValue可以转换为- double值。它将结果转换为- double值然后进行比较。
- WCEUNIT_DOUBLE_BETWEEN(testValue, minValue, maxValue)- 此宏测试 - testValue是否小于- maxValue且大于- minValue。假定三个表达式值都被评估为- double。以下代码展示了上述宏的用法。
以下代码展示了上述宏在 CComplex 类的测试用例中的用法。
void CTestComplex::testAdd()
{
    CComplex temp;
    temp = *m_p1 + *m_p2;
    WCEUNIT_ASSERT(CComplex(30, 70) == temp);
}
void CTestComplex::testEqual()
{
    CComplex temp2;            //Below test should fail.
    WCEUNIT_ASSERT_FAIL(temp2 == *m_p3);
    CComplex temp1;            //Below test should pass.
    temp1 = *m_p1 + *m_p2;
    WCEUNIT_ASSERT_EQUALS(temp1, *m_p3);
    //To check the equality of the real and imaginary members.
    WCEUNIT_DOUBLE_EQUALS(temp1.GetReal(), m_p3->GetReal());
    WCEUNIT_DOUBLE_EQUALS(temp1.GetImaginary(), m_p3->GetImaginary());
}
void CTestComplex::testException()
{
    //This fuction is only to demonstrate the exception test.
    //It throws CUserException. This test will fail as the expected exception
    //is CFileException.
    CUserException e;
    THROW(&e);
}
void CTestComplex::InitializeTest()
{
    m_p1 = new CComplex(10, 20);
    m_p2 = new CComplex(20, 50);
    m_p3 = new CComplex(30, 70);
}
void CTestComplex::CleanUpTest()
{
    delete m_p1;
    delete m_p2;
    delete m_p3;
}
运行测试用例
CTestRunner 类负责运行测试用例并获取测试结果。要运行您编写的测试用例,请创建 CTestRunner 类的实例。还要创建所有测试套件类的实例,并将每个实例的地址传递给 CTestRunner 类的 AddTestSuite() 函数。然后调用 CTestRunner 的 Start() 函数。以下代码显示了如何运行测试用例。
BOOL CTestComplexApp::InitInstance()
{
    //Create the instance of CTestRunner class.
    CTestRunner obj;
    //Create the instance of CTestComplex test suite class.
    CTestComplex test1;
    //Pass the address of CTestComplex instance to the AddTestSuite() function.
    obj.AddTestSuite(&test1);
    //Sart the WCEUnit dialog to run the test cases.
    obj.Start();
    return FALSE;
}
图形用户界面

上图显示了主对话框。要选择要运行的测试用例,请单击 **Select** 按钮,将出现一个新对话框,其中显示了您编写的测试用例的树状视图。您可以选择单个测试用例、测试套件或 **All Tests**。如果您想运行某个测试套件的所有测试用例,请选择该测试套件;如果您想运行所有测试套件的所有测试用例,请选择根节点 **All Tests**。 **Init once** 复选框用于选择 InitializeTest() 和 CleanUpTest() 的调用方式。如果选中,则对于每个测试套件,IntializeTest() 和 CleanUpTest() 分别在所有测试用例执行之前和之后仅调用一次。如果未选中,则对于每个测试用例都会调用这两个函数。单击 **Run** 按钮来运行选定的测试用例。结果显示在列表框中。如果您想查看更多信息,只需单击结果项,您将看到详细的结果。如果您想将测试结果保存到文本文件中,请选中 **Make log** 复选框,将创建一个名为 \\WCEUnitLog.txt 的文件,其中包含详细的测试结果信息。
关注点
在此项目开发过程中,我学到了很多关于 C++ 中成员函数指针的知识。它们非常强大,能做很多超出您想象的事情。也许下次,我会尝试写一些关于成员函数指针的文章。


