Google发布了很多开源的软件,最近尝试了一下Google的单元测试工具GTest,发现很有特色。这里有个不错的入门教程。GTest有个特色很有意思就是测试方法不需要在头文件定义,直接把实现写在代码里面就行了。写好的测试用例(其实就是一个过程)自动注册到单元测试引擎,不需要手工注册。但GTest在结果呈现方面比较弱,我所知道只有命令行输出和XML输出。CppUnit在这方面明显强很多,有个UI工具可用来选择执行测试用例并展示结果。回到GTest,本着方便实用的原则,首选当然是命令行输出了,XML输出等项目大了再用吧。
前面提到的教程是基于SDK的,而在MFC GUI程序下面使用GTest还是有些区别的,好了,废话少说,Action!
一,编译GTest库文件
首先我们需要为MFC编译GTest库。VS下面打开gtest工程文件,打开Configuration Manager,从Debug配置复制两份新配置,分别起名为 Debug-MDd 和 Debug-MTd. 然后到项目管理器里面重设所有项目的属性,如图分别设置相应的 Runtime Library。
编译两次Solution,生成gtest-1.3.0/msvc/Debug-MDd 和gtest-1.3.0/msvc/Debug-MTd
二,新建一个MFC项目
再次打开Configuration Manager,从Debug配置复制一份新配置起名为Debug-Test, 然后设置项目配置
添加gtest-1.3.0/include 到项目的头文件路径里,并设置一个预定义GTEST
三,到 xxxxApp::InitInstance() 添加代码,把测试启动代码和MFC程序启动代码分开,这样以后选择Debug就是普通模式下运行MFC程序,选择Debug-Test就是运行测试用例。如图:
- BOOL CPockerClientApp::InitInstance()
- {
- #ifdef GTEST
- CConsole cc;
- int argc = 0;
- TCHAR* argv = _T("");
- testing::InitGoogleTest(&argc, &argv);
- RUN_ALL_TESTS();
- HWND hwnd = GetConsoleWindow();
- SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
- AfxMessageBox(_T("about to exit!"));
- return false;
- #else
- // 如果一个运行在 Windows XP 上的应用程序清单指定要
- // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
- //则需要 InitCommonControlsEx()。否则,将无法创建窗口。
- INITCOMMONCONTROLSEX InitCtrls;
- InitCtrls.dwSize = sizeof(InitCtrls);
- // 将它设置为包括所有要在应用程序中使用的
- // 公共控件类。
- InitCtrls.dwICC = ICC_WIN95_CLASSES;
- ......
- ......
- // 唯一的一个窗口已初始化,因此显示它并对其进行更新
- m_pMainWnd->ShowWindow(SW_SHOW);
- m_pMainWnd->UpdateWindow();
- // 仅当具有后缀时才调用 DragAcceptFiles
- // 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
- return TRUE;
- #endif //GTEST
- }
四,定义了一个CConsole 实例。
因为MFC GUI程序默认是没有控制台的,所以我们就看不到GTest的输出。CConsole 就是用来创建一个控制台对象并注册为默认控制台。代码如下:
- class CConsole
- {
- public:
- CConsole(void);
- virtual ~CConsole(void);
- private:
- };
- #include "StdAfx.h"
- #include "Console.h"
- #include <conio.h>
- #include <fcntl.h>
- #include <io.h>
- CConsole::CConsole(void)
- {
- AllocConsole();
- int hCrun;
- hCrun = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
- FILE* hFile = _fdopen(hCrun, "w");
- // use default stream buffer
- setvbuf(hFile, NULL, _IONBF, 0);
- *stdout = *hFile;
- //test
- //_cprintf("test console by _cprintf/n", 0);
- //std::cout << "test console by std::out/n";
- }
- CConsole::~CConsole(void)
- {
- FreeConsole();
- }
测试执行完成以后,把Console窗口移到前台,并通过MessageBox阻止程序退出,便于查看控制台的输出。
五,输出效果
怎么样,还不错吧。顺便说一下本文说的MFC程序指GUI MFC Dialog程序或Document View程序,不包括MFC控制台程序 :P
六,补记
使用TDD开发方法意味着需要频繁运行测试,建议大家在自己VS的界面上添加一个Start Without Debug的图标,知道怎么做吧,我就不多说了。
七,再补
这段时间试用下来发现GTest不能选择部分测试用例单独测试挺不爽的,特别是写了好多测试用例,这些用例都依赖了具体实现的某个方法,但只有其中几个Fail了,这种情况下debug就太麻烦了,虽然可以用条件断点之类的手段,毕竟没有CPPUnit那样按需选择方便。从这个角度看,GTest还不适合在测试用例比较多的项目中应用。