在没有虚拟方法的情况下模拟 class
Mocking class with no virtual methods
我刚刚开始使用 google mock 对生产代码进行单元测试。我想模拟的 classes 里面没有虚方法。通过阅读一些关于 google mock 的内容,是否不可能像这样模拟 class?请记住,我不想对源代码进行任何更改。
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()
的弱 linked 副本放入与调用它的函数相同的目标文件。在第二种情况下,您可以使用 linker 技巧替换它,具体取决于您使用的平台 - 直到稍后编译器切换到最终决定内联它。
如果只是 class Test
你不想修改,但是你可以更改使用 Test
作为依赖的代码并进行测试通过单元测试,你可以做模板依赖注入。但问题听起来你可能也不想修改该代码。
现在假设该函数不是内联的,并且在某个文件 "Test.cpp" 中定义,并且 class 是多态的(由于虚拟析构函数,它在示例中)您可以替换该文件中的所有定义,使它们表现得好像它们是虚拟的,即使它们不是:
像往常一样编写 Google Mock class,使用您希望能够检测到的函数进行模拟。
#include "Test.hpp"
#include <gmock/gmock.h>
class MockTest : public Test
{
public:
MOCK_METHOD0(Add, int());
MOCK_CONST_METHOD0(Print, void());
};
(我在示例中添加了一个 const 方法,以展示如何处理它们。)
在同一个单元测试代码中,像这样为模拟函数编写定义。
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 文件中的其他定义编写存根,以使 linker 满意。这并不一定意味着它们实际上会被调用或以其他方式使用。
- 当link进行单元测试时,确保没有提供真正的Test.cpp文件。如果它通常是库的一部分,您可能需要在命令行中列出该库中的其他文件。这可能会导致依赖顺序问题 and/or 循环依赖问题。 GNU 的 linker 有“-Wl,--start-group ... -Wl,--end-group”通过重复尝试一些对象 and/or 库来处理循环 link 问题一个循环,直到尽可能多地被解决;我不确定其他系统。
我刚刚开始使用 google mock 对生产代码进行单元测试。我想模拟的 classes 里面没有虚方法。通过阅读一些关于 google mock 的内容,是否不可能像这样模拟 class?请记住,我不想对源代码进行任何更改。
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()
的弱 linked 副本放入与调用它的函数相同的目标文件。在第二种情况下,您可以使用 linker 技巧替换它,具体取决于您使用的平台 - 直到稍后编译器切换到最终决定内联它。
如果只是 class Test
你不想修改,但是你可以更改使用 Test
作为依赖的代码并进行测试通过单元测试,你可以做模板依赖注入。但问题听起来你可能也不想修改该代码。
现在假设该函数不是内联的,并且在某个文件 "Test.cpp" 中定义,并且 class 是多态的(由于虚拟析构函数,它在示例中)您可以替换该文件中的所有定义,使它们表现得好像它们是虚拟的,即使它们不是:
像往常一样编写 Google Mock class,使用您希望能够检测到的函数进行模拟。
#include "Test.hpp" #include <gmock/gmock.h> class MockTest : public Test { public: MOCK_METHOD0(Add, int()); MOCK_CONST_METHOD0(Print, void()); };
(我在示例中添加了一个 const 方法,以展示如何处理它们。)
在同一个单元测试代码中,像这样为模拟函数编写定义。
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 文件中的其他定义编写存根,以使 linker 满意。这并不一定意味着它们实际上会被调用或以其他方式使用。
- 当link进行单元测试时,确保没有提供真正的Test.cpp文件。如果它通常是库的一部分,您可能需要在命令行中列出该库中的其他文件。这可能会导致依赖顺序问题 and/or 循环依赖问题。 GNU 的 linker 有“-Wl,--start-group ... -Wl,--end-group”通过重复尝试一些对象 and/or 库来处理循环 link 问题一个循环,直到尽可能多地被解决;我不确定其他系统。