'Is Googletest with EXPECT_CALL and ASSERT_DEATH afterwards possible?

I'm trying to design a test that calls a function on a test object and then expects two things to happen in sequence:

  1. The test object makes a nested call on another object (stored as a reference in the test object)
  2. The test object exits (e.g. using std::exit with non-zero exit code)

Currently, I'm trying to use EXPECT_CALL and ASSERT_DEATH but since the program exits, my mock expectations are never verified (the mock "leaks").

Test code:

ASSERT_DEATH(EXPECT_CALL(MyMock, InjectedMethod()).Times(1); MyObject.CallInjectedReferenceAndCrash(), "");

Test result:

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TestExpectCallAssertDeath
[ RUN      ] TestExpectCallAssertDeath.MyTest

../TestExpectCallAssertDeath.cpp:50: ERROR: this mock object (used in test TestExpectCallAssertDeath.MyTest) should be deleted but never is. Its address is @0x4c6fb9c.
ERROR: 1 leaked mock object found at program exit. Expectations on a mock object is verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
[       OK ] TestExpectCallAssertDeath.MyTest (304 ms)
[----------] 1 test from TestExpectCallAssertDeath (308 ms total)

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

Interestingly, the test passes but obviously, this is not a good test.

One way to get around this is to inject a fake that changes the object under test to throw a C++ exception instead of exiting, and then use ASSERT_THROW instead of ASSERT_DEATH.

Test code:

ASSERT_THROW(EXPECT_CALL(MyMock, InjectedMethod()).Times(1); MyObject.CallInjectedReferenceAndCrash(), std::exception);

Test result:

[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TestExpectCallAssertDeath
[ RUN      ] TestExpectCallAssertDeath.MyTest
[       OK ] TestExpectCallAssertDeath.MyTest (24 ms)
[----------] 1 test from TestExpectCallAssertDeath (30 ms total)

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

I'd like to avoid using the exception strategy if possible. Any alternative ideas?



Solution 1:[1]

One way to solve this could be to use pattern-matching of the death tests. Unfortunately, GoogleTest only supports POSIX extended flavour (no negative lookahaeds, which would be very useful here, nor quantifiers). And that's only on POSIX systems, on Windows it supports only a subset of extended POSIX.

You can make your mock function output to std::cerr and check that output (see it online):

struct Mock
{
    MOCK_METHOD(void, mockedMethod, (), ());
};

struct ClassUnderTest
{
    Mock* mock;
    void methodUnderTest() {
        mock->mockedMethod();
        mock->mockedMethod();
        mock->mockedMethod();
        std::exit(1);
    }
};

TEST(MyDeathTest, workaround)
{
    Mock m;
    ClassUnderTest uut {&m};
    EXPECT_CALL(m, mockedMethod()).WillRepeatedly(::testing::Invoke([]() {
        static int count = 1;
        std::cerr << "mockedMethod" << count++;}));
    ASSERT_DEATH({uut.methodUnderTest();}, "mockedMethod2^");
}

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

Since there are no lookarounds, you cannot check if something was not called, essentially limiting yourself to what NiceMock would do (but you need to keep order of final method calls correct) or to checking what ::testing::InSequence would check.

Also, this workaround will still make GMock print error you noticed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yksisarvinen