为什么GoogleMock会泄漏我的shared_ptr?

37

我使用GoogleMock/GoogleTest进行测试,当一个matcher带有一个指向mock的shared_ptr参数,并且对相同的shared_ptr调用EXPECT时,会出现一些奇怪的行为。以下是有问题的代码:

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace boost;
using namespace testing;

struct MyParameter
{
    virtual ~MyParameter() {}
    virtual void myMethod() = 0;
};

struct MyParameterMock : public MyParameter
{
    MOCK_METHOD0(myMethod, void());
};

struct MyClass
{
    virtual ~MyClass() {}
    virtual void myMethod(shared_ptr<MyParameter> p) {}
};

struct MyClassMock : public MyClass
{
    MOCK_METHOD1(myMethod, void(shared_ptr<MyParameter>));
};

TEST(LeakTest, GoogleMockLeaksMatchedPointer)
{
    shared_ptr<MyClassMock> c = make_shared<MyClassMock>();
    shared_ptr<MyParameterMock> p = make_shared<MyParameterMock>();
    {
        InSequence dummy;
        EXPECT_CALL(*c, myMethod(Eq(p)));
        EXPECT_CALL(*p, myMethod());
    }
    c->myMethod(p);
    p->myMethod();
}

测试运行时,我得到了

leak_ptr_mock.cpp:37: ERROR: this mock object (used in test LeakTest.GoogleMockLeaksMatchedPointer) should be deleted but never is. Its address is @0x9309544.
ERROR: 1 leaked mock object found at program exit.
任何想法为什么会发生这种情况?我宁愿不使用 Mock::AllowLeak
1个回答

41
这是将p作为shared_ptr持有,并使用InSequence以及您声明期望的顺序所产生的结果。
当您调用时:
    EXPECT_CALL(*c, myMethod(Eq(p)));

增加puse_count。为了通过泄漏检测,p必须在TEST结束时或之前被销毁。

问题在于,在内部,gmock通过持有指向前一个期望的指针来维护所需的模拟调用序列的记录。所以当你调用EXPECT_CALL(*p, myMethod());时,它会获取前一个期望的指针的副本。

这将导致在TEST结束时阻塞对p的析构函数的调用。

为了解决这个问题,我认为你最好调用:

    EXPECT_TRUE(Mock::VerifyAndClearExpectations(p.get()));

在退出之前,加上TEST。这会清除对p的期望,包括关键的前提期望,从而允许正确地调用p的析构函数。

或者,如果模拟调用的顺序不重要,只需删除InSequence dummy;也可以使p的析构函数执行。


另外,您的代码存在一些问题:

  • 基本结构体应该有虚拟析构函数
  • MyClass::myMethod 应该是虚拟的,以允许gmock的函数覆盖它
  • p->myMethod(p); 应该是 p->myMethod();

它有效了,弗雷泽!我按照你的建议纠正了代码。 - bruno nery
@bruno nery: 你使用的是哪个版本的 GoogleMock? - Martin
1
如果在创建c之前创建p会发生什么?那么最终c不是会被销毁,其期望得到验证和清除,这将导致p的引用计数减少。之后p将被销毁,验证并完全销毁,因为计数器现在为0。 - Martin
1
@Martin 不,构造的顺序不会影响检查的顺序,因为使用了 InSequence dummy;。交换 EXPECT_CALL 的顺序将允许测试成功退出而没有泄漏,但如果期望的顺序无关紧要,那么你最好根本不使用 InSequence。顺便说一下,我正在使用 gmock 1.6.0。 - Fraser
如果 p 是一个结构体,会发生什么? - Mike
如果我的shared_pointer不是显式指定的,而是隐式指定的,我该怎么办?例如,使用_。 - Calmarius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接