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++ 中成员函数指针的知识。它们非常强大,能做很多超出您想象的事情。也许下次,我会尝试写一些关于成员函数指针的文章。