谷歌模拟全局模拟对象内存泄漏

时间:2015-02-12 11:07:48

标签: c++ unit-testing memory-leaks heap googlemock

我正在使用VS2005和C ++进行使用谷歌模拟的单元测试 我在单元测试中有一个全局自由函数,我使用以下代码来模拟自由函数:

NiceMock <MockA> mockObj;  



struct IFoo {  
    virtual A* foo() = 0;  
    virtual ~IFoo() {}  
};  

struct FooMock : public IFoo {  
    FooMock() {}  
    virtual ~FooMock() {}  
    MOCK_METHOD0(foo, A*());  
};

FooMock fooMock;

// foo() implementation  
A* foo() {  
    return fooMock.foo();  
}  

SetUp()函数中,我在全局对象上设置了Expectations,如

EXPECT_CALL(fooMock,foo())  
    .Times(1)  
    .WillOnce(Return(&mockObj));  

TEST(..., instA) {

    // ...
}

并在TearDown()中删除全局模拟对象fooMock

virtual TearDown(){  
    delete &fooMock;  
}  

当我运行代码时,我收到以下错误

  

错误:xyz.instA中的内存泄漏,

也,
0个空闲块中的0个字节 -1个正常块中的-61个字节 7个CRT块中的68个字节 0忽略块中的0字节 0个客户端块中的0个字节 使用的最大数量:11025字节
总分配:50602字节。

谁能告诉我这里发生了什么?如果我不删除fooMock,我会收到错误“fooMock应该被删除但从不是”,或者检测到堆损坏。
从错误中,我可以看到我的堆被错误处理,但我找不到重点。我也试图一步一步地调试它。

一些帮助真的很棒! :)

4 个答案:

答案 0 :(得分:1)

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

在上面的示例中,您只需在测试中实例化fooMock:

TEST(..., instA) {

    FooMock fooMock;
    // ...
}

答案 1 :(得分:1)

我遇到了同样的问题,这是解决方案(不确定内部到底发生了什么,因为全局模拟 obj 最终会被破坏)。

std::unique_ptr ptrMockObj(new FooMock());

在测试用例的最后,删除mock obj ptr

测试(...,instA) { ... ptrMock.Obj.reset() }

答案 2 :(得分:0)

正如Ian所述:

  

Googlemock / googletest希望模拟在测试体内或测试夹具类中定义。

Cookbook

解释了背后的想法
  

当它被销毁时,你友好的模拟对象会自动验证对它的所有期望是否已经满足,如果没有,将生成Google Test失败。这很方便,因为它让您不必担心。也就是说,除非你不确定你的模拟对象是否会被销毁。

在你的情况下,fooMock是一个全局变量(正如你所声明的那样,它必须保持这种状态)所以在每次测试之后你只需要运行手动验证:

    using ::testing::Mock;

    TEST(..., instA) 
    {
       ASSERT_TRUE(
           Mock::VerifyAndClearExpectations(
            &fooMock));
    }

因为它是一个全局变量,你也可以这样做:

Mock::AllowLeak(&fooMock);

Cookbook - Forcing a VerificationCheatSheet - Verifying and Resetting a Mock中的更多详细信息:

答案 3 :(得分:0)

所以我偶然发现了这个问题,无法解决。但是后来成为一名软件工程师,我需要找到解决方案。所以这就是我所做的。

假设您要模拟一个队列。您要模拟的一个功能是出队。我将假设您想使整数出队以使事情简单,因为我想演示如何制作全局模拟而不会导致内存泄漏。

class QueueInterface {
    public:
        virtual ~QueueInterface() {};
        virtual int dequeue() = 0;
};

class QueueMock : public QueueInterface {
    public:
        virtual ~QueueMock() {};
        MOCK_METHOD(int, dequeue, (), (override));
};

// Instead of a global object, have a global pointer
QueueMock *globalQueue;

class TestFixture : public ::testing::Test {
    protected:
        QueueMock mockedQueue;

        void SetUp() {
            globalQueue = &mockedQueue; // Point the global pointer to this queue
        }

        void TearDown() {
            globalQueue = NULL;
        }
}

// Now you can use this global queue pointer in free function or
// C style functions and override the existing implementations.
// This way you can mock a global object.

int dequeueFromQueue() {
   return globalQueue->dequeue();
}

TEST_F(TestFixture, DEQUEUE_TEST) {
    // Write your test here to use global queue pointer
    // Deref pointer to get mocked object
    EXPECT_CALL(*globalQueue, dequeue);
}

通过这种方式,无论何时执行新测试,都会分配成员变量嘲笑队列,然后在测试结束时将其释放。 globalQueue每次都会指向每个测试的成员实例。

希望这会有所帮助! :)