Can
we verify that a mock object is properly destroyed? Of course! There is
a couple of subtle differences between mocking regular functions and
mocking destructors. Let's suppose we have a Grinder object that destroys any Piece object passed to its grind method:
void Grinder::grind(Piece * piece) {
delete piece;
}
Furthermore,
Grinder can accumulate a list of Pieces (actually, let it be a list of
pointers to Pieces) for destruction that will take place when Grinder
itself is destroyed. To add a Piece to the list, we define:
int Grinder::enqueue_piece(Piece * piece) {
list_of_pieces_.push_back(piece);
return list_of_pieces_.size();
}
Now, to keep the promise of destroying the queued pieces, Grinder's destructor is defined as follows:
Grinder::~Grinder() {
for(list<Piece*>::iterator it = list_of_pieces_.begin(); it != list_of_pieces_.end(); it++) {
delete *it;
}
}
But
how can we mock the destructor of Piece so that we can verify that
Grinder really destroys Pieces in both scenarios (on grind method call
and on Grinder destruction)? Well, we can't really mock Piece's
destructor itself, but we can use a workaround: it's enough to define
MockPiece destructor so that it calls another function, such as destroy,
that will be used as a signal for us that the destructor has been
called.
class Piece {
public:
virtual ~Piece() {}
};
class MockPiece : public Piece {
public:
MOCK_METHOD0(destroy, void());
virtual ~MockPiece() { destroy(); }
};
Now, a test like:
TEST(Grinder, CanGrindPiece) {
MockPiece * piece = new MockPiece;
Grinder grinder;
EXPECT_CALL(*piece, destroy());
grinder.grind(piece);
}
Will pass correctly (we know that grind method deletes the object passed as a pointer argument). But what about this test:
TEST(Grinder, CanGrindWhenDies) {
MockPiece * p1 = new MockPiece;
MockPiece * p2 = new MockPiece;
MockPiece * p3 = new MockPiece;
list<Piece*> list_of_pieces;
list_of_pieces.push_back(p1);
list_of_pieces.push_back(p2);
list_of_pieces.push_back(p3);
Grinder grinder;
EXPECT_CALL(*p1, destroy());
EXPECT_CALL(*p2, destroy());
EXPECT_CALL(*p3, destroy());
grinder.enqueue_piece(p1);
grinder.enqueue_piece(p2);
grinder.enqueue_piece(p3);
//Grinder dies after this line
}
If
you try out test example above with the correct implementation of
Grinder's destructor, it will pass. But try to comment out the delete
statement in Grinder's destructor and see what happens. Google Mock
prints error messages like:
.//Grinder_test.cpp:34:
ERROR: this mock object (used in test Grinder.CanGrindWhenDies) should
be deleted but never is. Its address is @0x809d5e0.
but it
still reports that the test itself passed! We would like to have Google
Mock report test failure in this case, wouldn't we? The trouble is that
Grinder dies when our test is already finished (when it goes out of
TEST scope - it is an automatic object). The workaround here is to add
another "helper" test that will verify our expectations after
the proper test has been finished. Let's do the following modification:
move MockPieces p1, p2 and p3 from CanGrindWhenDies test to global
scope. Then, let's add a short helper test:
TEST {
::testing::Mock::VerifyAndClearExpectations(p1);
::testing::Mock::VerifyAndClearExpectations(p1);
::testing::Mock::VerifyAndClearExpectations(p1);
}
That's
it: this implementation of expectations and verification will yield
correct results ("helper" test passing or failing) depending on whether
Grinder's destructor is implemented correctly.
Exercise: try to
move VerifyAndClearExpectations statements to CanGrindWhenDies test and
see what happens when you run the test with correct and incorrect
implementation of Grinder's destructor.
I have had the rare opportunity of watching and being part of the change that the software industry has gone through throughout over 20 last years. This blog is a collection of my reflections on pursuing agility path. It includes observations and advice regarding development teams and notes on useful engineering practices. Enjoy! Piotr Górak.
See also
-
We may often come across a piece of code that was written without Unit Tests at all. In addition, the piece of code may be dealing with IO l...
-
Google Mock provides several ways to maintain state inside mock objects. One way of implementing state maintenance is with SaveArg . Consid...
-
Requirements have a long history in software industry. We all have heard or read terms like: requirements definition, requirements managemen...
-
Google Mock provides a way to return newly created objects from a mock method. Suppose we have a Generator class that is supposed to ...
-
Can we verify that a mock object is properly destroyed? Of course! There is a couple of subtle differences between mocking regular func...