【问题标题】:Mock non-virtual method C++ (gmock)模拟非虚方法 C++ (gmock)
【发布时间】:2011-04-25 11:05:22
【问题描述】:

我有课

class CSumWnd : public CBaseWnd
{

 private:
 bool MethodA()
}

请您帮助如何在不制作虚拟的情况下模拟MethodA()hi-perf dependency injection的概念我没看懂

【问题讨论】:

  • 您的链接已失效-您可以更新它吗?我找不到完全相同的页面,也许this one 就足够了吗?

标签: c++ mocking gmock


【解决方案1】:

这意味着您必须将生产代码模板化。使用您的示例:

CSumWind类定义:

class CSumWnd : public CBaseWnd
{

 private:
 bool MethodA()
};

嘲笑CSumWnd类定义:

class MockCSumWnd : public CBaseWnd
{

 private:
 MOCK_METHOD(MethodA, bool());
};

必须使用模拟类CSumWind 测试的生产类。现在它被模板化为在生产代码中使用CSumWind 类和在测试中使用MockCSumWnd 类。

template <class CSumWndClass>
class TestedClass {
//...
   void useSumWnd(const CSumWndClass &a);

private:
  CSumWndClass sumWnd;
};

TestedClass 在生产环境中的实例化:

TestedClass <CSumWnd> obj;

在测试可执行文件中实例化TestedClass 对象:

TestedClass <MockCSumWnd> testObj;

【讨论】:

  • 为了保持你的“生产”代码干净,我发现这样做很有帮助:模板 class TestedClassTemplate { ... },然后执行 typedef TestedClassTemplate TestedClass;跨度>
  • 请参阅stackoverflow.com/q/1127918/49972,了解按照您的建议行事的后果。
  • 我在MOCK_METHOD(MethodA, bool()); stackoverflow.com/questions/46542373/… 遇到问题我收到上述问题中指定的错误
  • MOCK_METHOD 应替换为 MOCK_METHOD0 // 0 个参数。应该有“;”在“bool MethodA()”之后也是如此。不幸的是,这个想法对我来说仍然不清楚......
  • 介意编写方法的主体,以明确它应该做什么? void useSumWnd(const CSumWndClass &a);
【解决方案2】:

如果您不想更改现有代码,这里是我正在研究的 VC++ 的特定解决方案 (https://github.com/mazong1123/injectorpp)。简单的步骤是:

  1. 利用 DbgHelp.h 检索类的所有方法的符号和内存地址。基本上它会在运行时从 .pdb 文件中检索元信息。
  2. 将用户输入的 to-mock 方法符号与步骤 1 的输出进行比较,得到 to-mock 方法的内存地址。
  3. 利用 windows api WriteProcessMemory 更改 to-mock 方法的入口字节,类似于:__asm {move eax, 1; ret}。

让我们在这里输入关键代码。

  1. 检索类的方法符号和地址。下面是实现的关键思想。完整的源代码在https://github.com/mazong1123/injectorpp/blob/master/injectorpp/ClassResolver.cpp

    // Retrieve class symbol.
    if (SymGetTypeFromName(this->m_hProcess, modBase, className.c_str(), classSymbol) == FALSE)
    {
        throw;
    }
    
    // Get children of class - which are methods.
    DWORD numChildren = 0;
    if (SymGetTypeInfo(this->m_hProcess, classSymbol->ModBase, classSymbol->TypeIndex, TI_GET_CHILDRENCOUNT, &numChildren) == FALSE)
    {
        throw;
    }
    
    // Get methods info.
    if (SymGetTypeInfo(this->m_hProcess, classSymbol->ModBase, classSymbol->TypeIndex, TI_FINDCHILDREN, methods) == FALSE)
    {
        throw;
    }
    
    // Retrieve all methods.
    for (DWORD i = 0; i < numChildren; ++i)
    {
        ULONG curChild = methods->ChildId[i];
    
        // Resolve function.
        Function resolvedFunction;
        this->m_functionResolver->Resolve(classSymbol->ModBase, curChild, resolvedFunction);
    
        // Add the resolved function to the output.
        resolvedMethods.push_back(resolvedFunction);
    }
    
  2. 步骤 2 很简单。它只是文本比较和处理。

  3. 如何注入神奇的asm来改变方法行为:(完整的源代码在https://github.com/mazong1123/injectorpp/blob/master/injectorpp/BehaviorChanger.cpp

    // A magic function to change the function behavior at runtime
    //
    // funcAddress - The address of the function to be changed from.
    // expectedReturnValue - The return value should be changed to.
    void BehaviorChanger::ChangeFunctionReturnValue(ULONG64 funcAddress, int expectedReturnValue)
    {
    
    
    // The purpose of this method is to change the return value
    // to what ever int value we expected.
    
    // Therefore, we just need to inject below asm to the header of specific function:
    //
    // mov eax, expectedValue
    // ret
    //
    // Above asm code tells the function to return expectedValue immediately.
    
    // Now let's prepare the asm command.
    byte asmCommand[6];
    
    // mov
    asmCommand[0] = 0xB8;
    
    // The value.
    asmCommand[1] = expectedReturnValue & 0xFF;
    asmCommand[2] = (expectedReturnValue >> 8) & 0xFF;
    asmCommand[3] = (expectedReturnValue >> 16) & 0xFF;
    asmCommand[4] = (expectedReturnValue >> 24) & 0xFF;
    
    // ret
    asmCommand[5] = 0xC3;
    
    WriteProcessMemory((HANDLE)-1, (void*)funcAddress, asmCommand, 6, 0);
    }
    

【讨论】:

  • 看起来很有趣。我希望看到 GCC/Clang 有类似的东西。
  • @hedayat 我打算这样做。最近我几乎完成了 x86 Windows 部分。请继续关注。
【解决方案3】:

试试CppFreeMock 和其他一些提到的here

例子:

string func() {
    return "Non mocked.";
}

TEST(HelloWorld, First) {
    EXPECT_CALL(*MOCKER(func), MOCK_FUNCTION()).Times(Exactly(1))
        .WillOnce(Return("Hello world."));
    EXPECT_EQ("Hello world.", func());
}

【讨论】:

  • 我的第二个链接指向 OneDrive 登录。此外,与其仅使用“此处”作为描述,不如使用更具描述性的内容,以便在将来链接失效时仍可以找到该文档。
  • 我没有足够的声誉在一个答案中添加两个以上的链接,更新“这里”的链接,不再需要登录。完整的文档可以在 github 中找到。
  • 看起来无法用当前的 gmock 编译
  • @Louix 在我看来你是 CppFreeMock 的作者。您应该在答案中披露这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-25
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 2012-07-29
  • 2012-02-12
  • 2013-05-01
相关资源
最近更新 更多