死亡测试中奇怪的堆检查错误

时间:2012-03-02 20:33:48

标签: c++ memory-management googletest googlemock

我的一个googletest单元测试使用模拟对象和死亡测试时出现问题。这是一个最小化的代码示例,用于说明问题:

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

using namespace ::testing;

class MockA {
  public:
    MockA() {};
    virtual ~MockA() {};

    MOCK_METHOD1(bla,int(int));
};


class B {
  public:
    B(MockA * a)
            : a_(a) {};
    void kill() {
        exit(1);
    }
    MockA * a_;
};

TEST(BDeathTest,BDies) {
    MockA * a = new MockA();
    ON_CALL(*a,bla(_)).WillByDefault(Return(1));
    B * b = new B(a);
    EXPECT_DEATH(b->kill(),"");
    delete a;
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

输出:

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from BDeathTest
[ RUN      ] BDeathTest.BDies

gtest.cc:27: ERROR: this mock object (used in test BDeathTest.BDies) should be deleted but never is. Its address is @0x7fe453c00ec0.
ERROR: 1 leaked mock object found at program exit.
[       OK ] BDeathTest.BDies (2 ms)
[----------] 1 test from BDeathTest (2 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (2 ms total)
[  PASSED  ] 1 test.

似乎googlemock在EXPECT_DEATH断言后立即检查堆上的左侧模拟对象,但在调用宏之前删除a显然不是一个好的解决方案,因为a可能在被调用的函数中使用。我实际上希望检查在测试套件解构结束时发生。我错过了什么?

1 个答案:

答案 0 :(得分:2)

由于a将被泄露,你需要告诉gmock:

TEST(BDeathTest,BDies) {
    MockA * a = new MockA;
    ON_CALL(*a,bla(_)).WillByDefault(Return(1));
    B * b = new B(a);
    Mock::AllowLeak(a);  // <=== Self-explanatory addition
    EXPECT_DEATH(b->kill(),"");
    delete a;
    delete b;
}


您也可以使用::testing::FLAGS_gmock_catch_leaked_mocks = false;关闭所有gmock泄漏检测,但这可能是一个不好的习惯。但是,在这种情况下,如果在调用exit()时有大量模拟对象,则可能是合适的。如果测试继续进行进一步的工作,在EXPECT_DEATH之后立即重新开启它也是值得的(尽管在上面的例子中,重新开启它是没有意义的。)

TEST(BDeathTest,BDies) {
    MockA * a = new MockA;
    ON_CALL(*a,bla(_)).WillByDefault(Return(1));
    B * b = new B(a);
    FLAGS_gmock_catch_leaked_mocks = false;  // <=== Switch off mock leak checking
    EXPECT_DEATH(b->kill(),"");
    FLAGS_gmock_catch_leaked_mocks = true;  // <=== Re-enable mock leak checking
                                            //      in case the test is refactored
    delete a;
    delete b;
}


最后,处理这种特殊情况的第三种方法是delete a_;中的B::kill()而不是允许它泄漏。

class B {
    ...
    void kill() {
        delete a_;
        exit(1);
    }
    MockA * a_;
};

TEST(BDeathTest,BDies) {
    MockA * a = new MockA;
    ON_CALL(*a,bla(_)).WillByDefault(Return(1));
    B * b = new B(a);
    EXPECT_DEATH(b->kill(),"");
    delete a;
    delete b;
}

由于gtest会产生一个执行死亡测试的新流程,因此您可以在a_之前删除exit,同时删除测试夹具中的a

然而,对于不知道gtest死亡测试如何工作的人来说,这看起来像是被删除两次的同一个变量,并且可能引起混淆。