Странная ошибка проверки кучи в тесте на смерть - PullRequest
2 голосов
/ 03 марта 2012

У меня проблема с одним из моих юнит-тестов 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 Ответ

2 голосов
/ 03 марта 2012

Поскольку 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 ing, а также удалить a внутри тестового прибора.

Однако для тех, кто не знает, как работают gtest тесты на смерть, это будет выглядеть, как одна и та же переменная, удаляемая дважды, и может вызвать путаницу.

...