2012-03-02 143 views
2

我有一個使用模擬對象和死亡測試的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檢查遺留堆上mock對象的EXPECT_DEATH斷言之後,但刪除a在調用宏之前顯然不是一個好的解決方案,因爲a可能在被調用的函數中使用。我實際上希望檢查發生在測試套件解構的結尾。我錯過了什麼?

+0

可能是子進程中的退出導致程序終止而不刪除模擬對象,但會導致模擬對象清理例程進行泄漏檢查。我會嘗試使用abort()而不是退出 – PlasmaHH 2012-03-02 20:56:19

+0

它實際上使用'abort()'工作,雖然這沒有多大幫助,因爲我想檢查生產代碼中的特定錯誤代碼。 – jupp0r 2012-03-02 21:23:04

+0

我不認爲死亡測試是爲了這個,他們應該只是保證有東西崩潰。也許你想拋出異常並驗證某個異常是否被拋出? – PlasmaHH 2012-03-02 21:30:36

回答

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; 
} 


最後,要處理這個特定的情況下,第三種方式是在B::kill()delete a_;而不是允許其泄漏。

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死亡測試如何工作的人來說,這看起來像是同一個變量被刪除兩次,並可能導致混淆。

+0

現在的問題是:爲什麼googlemock在死亡測試結束時檢查刪除的東西,這不是一個錯誤嗎? – jupp0r 2012-03-04 18:43:26

+0

我不這麼認爲。 gmock可能甚至不知道其對象的破壞是在gtest死亡測試的背景下。此外,死亡測試也可能正在測試一個優雅的應用程序退出,其中的對象被正確刪除。默認檢查泄漏似乎是gmock的最佳選擇,特別是因爲如果需要可以明確禁用此行爲。 – Fraser 2012-03-05 09:15:10