没有虚拟方法的模拟类

Mocking class with no virtual methods

本文关键字:模拟类 方法 虚拟      更新时间:2023-10-16

我刚刚开始使用googlemock对生产代码进行单元测试。我想模拟的类内部没有虚拟方法。从谷歌mock上读到的一点内容来看,难道不可能这样模拟一个班级吗?请记住,我不想对来源做任何更改。

class Test
{
Test(void);
virtual ~Test() {}
Add();
};
int Test::Add()
{
return 1;
}
class MockTest : public Test
{
public:
MOCK_METHOD0(Add,int(void));
};

可能有一种方法,但它不如覆盖虚拟函数的简单方法好。

首先,如果函数不是虚拟的并且是内联的,那么你很可能运气不好。当编译器看到对obj.Add()ptr->Add()的调用时,该函数不是虚拟的这一事实意味着它不需要担心Test::Add()以外的某个函数可能需要成为实际调用的函数。因此,它很可能直接内联了Add定义中的代码,在这种情况下,替换它几乎是不可能的,或者它将Test::Add()的弱链接副本放入与调用它的函数相同的对象文件中。在第二种情况下,您可以使用链接器技巧替换它,取决于您使用的平台-直到稍后,编译器才决定内联它。

如果只是您不想修改的类Test,但您可以更改使用Test作为依赖项并将由单元测试测试的代码,则可以进行模板依赖项注入。但这个问题听起来可能你也不想修改那个代码。

现在,假设函数不是内联的,并且是在某个文件"Test.cpp"中定义的,并且类是多态的(在本例中是因为虚拟析构函数),您可以替换该文件中的所有定义,使它们看起来像是虚拟的,即使它们不是:

  1. 像往常一样编写Google Mock类,其中包含您希望能够检测到Mock的函数。

    #include "Test.hpp"
    #include <gmock/gmock.h>
    class MockTest : public Test
    {
    public:
    MOCK_METHOD0(Add, int());
    MOCK_CONST_METHOD0(Print, void());
    };
    

(我在示例中添加了一个const方法,以显示如何处理它们。)

  1. 在相同的单元测试代码中,为模拟函数编写定义,如下所示。

    int Test::Add()
    {
    if (auto* mock = dynamic_cast<MockTest*>(this))
    return mock->Add();
    // Next comes what to do if the code ever calls Add on
    // a Test which is not actually a MockTest. This could
    // be a stub implementation, an actual implementation, or
    // could intentionally terminate, throw, or note a gtest error.
    ADD_FAILURE() << "Test is not a MockTest";
    return 0;
    }
    void Test::Print() const
    {
    if (auto* mock = dynamic_cast<const MockTest*>(this)) {
    mock->Print();
    return;
    }
    ADD_FAILURE() << "Test is not a MockTest";
    }
    

您可能还需要在Test.cpp文件中至少为其他定义编写存根,以使链接器满意。这并不一定意味着它们会被调用或以其他方式使用。

链接单元测试时,请确保未提供真正的test.cpp文件。如果它通常是库的一部分,则可能需要在命令行中列出该库中的其他文件。这可能会导致依赖顺序问题和/或循环依赖问题。GNU的链接器有"-Wl,--start group…-Wl,-end group",通过在循环中重复尝试一些对象和/或库来处理循环链接问题,直到尽可能多地得到解决;我不确定其他系统