C++ 如何使用googletest/mock测试基于MFC-CWnd的类?

C++ 如何使用googletest/mock测试基于MFC-CWnd的类?,c++,unit-testing,mfc,mocking,googletest,C++,Unit Testing,Mfc,Mocking,Googletest,我对TDD的想法是新的。我以前没有使用过任何测试框架。最近我开始阅读有关它的文章,并使用谷歌测试进行练习。 我的目标是在MFC开发的遗留代码库中启动TDD。大多数时候,我必须使用GUI控件——开发新的自定义控件,向现有的自定义控件添加功能等等。因此,我想自动化测试主要来自CWnd类的GUI类 我在VisualStudio中创建了一个win32 console项目进行测试,在创建项目时,我在“添加通用头文件”选项中勾选了MFC。Visual Studio项目向导已生成主函数并已创建CWinApp对

我对TDD的想法是新的。我以前没有使用过任何测试框架。最近我开始阅读有关它的文章,并使用谷歌测试进行练习。 我的目标是在MFC开发的遗留代码库中启动TDD。大多数时候,我必须使用GUI控件——开发新的自定义控件,向现有的自定义控件添加功能等等。因此,我想自动化测试主要来自CWnd类的GUI类

我在VisualStudio中创建了一个win32 console项目进行测试,在创建项目时,我在“添加通用头文件”选项中勾选了MFC。Visual Studio项目向导已生成主函数并已创建CWinApp对象。在main函数中,我添加了googletest的样板代码。我已经将实际项目(即将测试)和google测试(和模拟)库编译为.lib,并将其链接到测试项目。我已经成功地构建了测试项目。我可以从项目中测试简单的东西

下面是向导生成的代码(包括google测试样板代码)——

我面临的挑战是我无法创建实际的窗口,因为这需要有消息循环。我想模拟CWnd,以便能够基于已知假设测试特性。但是,我找不到模拟CWnd的方法,因为它有一些依赖于HWND的非虚拟成员函数。HWND仅在创建窗口时有效。另一个挑战是消息处理程序不是虚拟函数。因此,我不能模拟消息处理程序,如果不创建窗口,就不可能将消息路由到其处理程序

我需要思考如何解决这个问题。我可以不使用mock或其他东西创建实际的窗口吗?或者我可以创建窗口和路由消息


提前感谢。

我建议使用Visual Studio编码的UI测试自动化套件。它比Google Mock好得多。Visual Studio编码的UI测试本机支持MFC/Win32 GUI内容。

测试MFC应用程序时出现问题,因为大多数函数都没有导出。因此,我们应该将我们的源代码包含在GTEST项目中,并将其与MFC运行时链接。这是棘手的部分。我在中使用了VS2017“Google测试的测试适配器”和NuGet软件包:“googletest.v140.windesktop.static.rt dyn”,它终于起作用了。

很可能您需要mfc和消息循环。从VisualStudio向导生成的应用程序开始,覆盖CWinApp的InitInstance、OnIdle和ExitInstance虚拟函数更容易

您可能需要允许windows消息通过主窗口传输,因此这使您能够使用有限状态机(FST)运行测试。我在InitInstance中初始化了我的环境,并报告了ExistInstance的测试结果。我使用FST主要是为了设置下一个测试验证器(一个函数指针)(从被测试主体正在执行的任何操作中调用),并调用测试启动器函数来发送消息以启动下一个操作

我使用OnIdle开始测试,因为它只有在MFC系统稳定并设置好后才一直被调用


使用googletest、Boost test或更早版本的Visual Studio的缺点是单元测试框架需要驱动测试。它们不允许您提供FST来运行测试。我在DLL中使用代码时遇到了问题(全局函数在不同的DLL中看不到相同的全局地址空间-DLL中的单例模式问题)。

我看到了编码的UI,但我并不想模拟一些用户操作。为此,我们有质量保证工程师。我想做开发人员部分,即单元测试。我想确保每个单元都能正常工作。最重要的是,我的目的是引入TDD(测试驱动开发)并重构遗留代码。为此,一些相关书籍的作者推荐使用单元测试。VisualStudio附带了单元测试框架。因此,不需要安装第三方。您可以将编码的UI和单元测试框架结合起来,以获得很好的结果。
#include "stdafx.h"
#include "TestMFC.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// The one and only application object
CWinApp theApp;
using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(NULL);

    if (hModule != NULL)
    {
        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
        {
            // TODO: change error code to suit your needs
            _tprintf(_T("Fatal Error: MFC initialization failed\n"));
            nRetCode = 1;
        }
        else
        {
            // TODO: code your application's behavior here.
              testing::InitGoogleMock(&argc, argv);
              nRetCode = RUN_ALL_TESTS();
        }
    }
    else
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        nRetCode = 1;
    }
    return nRetCode;
}