donturner
donturner

Reputation: 19156

Why do EXPECT_CALL tests pass unexpectedly when using a dereferenced pointer?

I am just getting started with GoogleTest and GoogleMock. Reading the "for dummies" documentation the example tests a Painter class which depends on a Turtle:

Real object - Turtle.h

class Turtle {
public:
    virtual ~Turtle() {}
    virtual void PenDown() = 0;
};

Mock object - mock-turtle.h

class MockTurtle : public Turtle {
public:
    MOCK_METHOD0(PenDown, void());
};

Code under test - Painter.h

class Painter {
public:
    Painter(Turtle *turtle){};
};

Unit test - test_painter.cpp

This is intended to test whether the turtle.PenDown() method is called from the Painter constructor.

TEST(PainterTest, CanDrawSomething) {
    MockTurtle turtle;
    EXPECT_CALL(turtle, PenDown())
            .Times(AtLeast(1));
    Painter painter(&turtle);
}

This test correctly fails because PenDown() is never called.

But if I change the test to use a dereferenced pointer to MockTurtle it incorrectly passes.

TEST(PainterTest, CanDrawSomething) {
    MockTurtle *turtle = new MockTurtle();
    EXPECT_CALL(*turtle, PenDown())
            .Times(AtLeast(1));

    Painter painter(turtle);
}

Why does this test pass when using a dereferenced pointer? Nowhere in my code is PenDown() called.

For more context, I would like to use a pointer to MockTurtle so that I can initialise it in a test fixture so that other tests can use it.

Upvotes: 3

Views: 1558

Answers (2)

donturner
donturner

Reputation: 19156

In addition to @StoryTeller's excellent answer, I thought it would be useful to add some extra background, so no-one else is caught out by this issue.

I am using CLion as my test runner and because of this bug the error which was occurring after the tests have been run was not being displayed. Running my test binary from the terminal revealed it:

./test_painter --gtest_filter=* --gtest_color=no
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN      ] PainterTest.CanDrawSomething
[       OK ] PainterTest.CanDrawSomething (0 ms)
[----------] 1 test from PainterTest (0 ms total)

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

/Users/donturner/PCode/workspace-kinetis/blinky/tests/test_painter.cpp:13: ERROR: this mock object (used in test PainterTest.CanDrawSomething) should be deleted but never is. Its address is @0x7fc06f402720.
ERROR: 1 leaked mock object found at program exit.

So I was forgetting to delete my pointer. I added the following line to the end of my test:

delete turtle; 

And hey presto the test correctly fails:

$ ./test_painter --gtest_filter=* --gtest_color=no
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PainterTest
[ RUN      ] PainterTest.CanDrawSomething
/Users/donturner/PCode/workspace-kinetis/blinky/tests/test_painter.cpp:13: Failure
Actual function call count doesn't match EXPECT_CALL(*turtle, PenDown())...
         Expected: to be called at least once
           Actual: never called - unsatisfied and active
[  FAILED  ] PainterTest.CanDrawSomething (0 ms)
[----------] 1 test from PainterTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] PainterTest.CanDrawSomething

 1 FAILED TEST

If anyone can tell me why forgetting to delete a pointer results in PenDown() being called I would be most interested!

Upvotes: 3

You don't delete your pointer.

And it's not that forgetting to delete it is going to result in PenDown() being pushed. The member is never called. But it's the destructor of MockTurtle that reports the results to the framework.

When you leak it, nothing is reported. The framework thinks you ran an empty test (which vacuously passes), because it doesn't get any feedback.

When turtle is an object (not a pointer), that has automatic storage duration, its destructor is called automatically at scope exit. That's why the error is reported.

This is just GoogleMock utilizing RAII for the boilerplate.

Upvotes: 5

Related Questions