Google Mock:使用全局模拟对象可以吗?

时间:2015-08-28 08:29:55

标签: c++ unit-testing mocking googletest gmock

在关于 gmock 的所有文档中,我总是找到要在测试中实例化的模拟对象,如下所示:

TEST(Bim, Bam)
{
    MyMockClass myMockObj;
    EXPECT_CALL(MyMockObj, foo(_));
    ...
}

因此,每次测试都会创建和销毁对象。我相信每个测试夹具创建和销毁对象也是完美的。但我想知道是否也可以拥有模拟对象的文件全局实例,如下所示:

MyMockClass myMockObj;

TEST(Bim, Bam)
{
    EXPECT_CALL(MyMockObj, foo(_))
    ...
}

我试过了,到目前为止我绝对没有问题,这一切似乎都很好。但也许我应该知道什么?仅仅因为我偶然发现了this question,唯一的答案是:

  

...问题在于您正在实例化FooMock的全局实例。 Googlemock / googletest期望模拟在测试体内或测试夹具类中定义。

但是我在文档或其他任何确认这一点的地方找不到任何东西(我忽略了吗?)。

谢谢,Georg

PS:我需要使用全局模拟实例的原因将是另一个讨论的主题(参见我的this posting)。

3 个答案:

答案 0 :(得分:3)

你可以,但这不是一个好主意。

做这样的事情违反了UT的隔离原则。 此违规可能会导致测试意外失败/通过。

Gtest使用伪对象的析构函数来验证期望是否发生,这就是期望每个假对象将在测试主体中或在测试夹具类中创建和释放的原因。

如果您将假对象设为全局,那么它将在每个UT结束时释放,然后验证将无法执行,即使应该失败,测试也会通过。当你一起执行所有测试时,你的一些UT可能会失败/失败;在一个测试中,您希望方法x不会调用,而在另一个测试中,您希望该方法将调用;在一个UT中,你希望方法x将调用3次,但是该方法在测试中调用两次而在另一次测试中调用一次(测试应该失败但是它赢了......)

所以底线你永远不应该使用全局模拟,除非这个全局模拟仅用于防止空指针(你没有设置行为......)

答案 1 :(得分:1)

在追逐与我的模拟对象相关的错误时偶然发现了这个问题。在我的情况下,问题是模拟对象的构造函数在InitGoogleMock之前被调用,这似乎把东西送到了杂草中。

注意:我使用Google Mock和CppUnitTestFramework。

故障:

MockObject mock;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
    InitGoogleMock(argc, argv);
}

WIN:

MockObject *mock = nullptr;
TEST_MODULE_INITIALIZE(ModuleInitialize)
{
    InitGoogleMock(argc, argv);
    mock = new MockObject;
}

TEST_MODULE_CLEANUP(ModuleCleanup)
{
    delete mock;
}

不是说它的最佳做法或任何事情,但如果你需要全局模拟对象我会说要注意你的构造函数何时被调用。

答案 2 :(得分:0)

除了可接受的答案外,如果您使用的是GTest,则在执行测试用例后不销毁全局变量时,它们也会被标记为泄漏。 此参考文献中包含泄漏的想法:https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#forcing-a-verification

如果您不想手动验证,则最接近的解决方案是将模拟对象作为您的灯具类的成员。而且,如果由于某种原因需要动态分配模拟,则可以有一个指针,并在SetUp和TearDown上创建/销毁实例(与@Chris Olsen的答案相同的概念)。或者,如果您使用的是C ++ 11,则可以使用shared_ptr:

class Fixture : public ::testing::Test 
{  
    std::shared_ptr<ObjT> mPtr;
    ...
    void SetUp()
    {
        mPtr = std::make_shared<ObjT>();
    }
    ...
}