I hate a bit to mention but most of the time you don't need a full blown mock object libray. Often a more or less quick and dirty approach suffices if you do the right thing. In such cases I don't care too much about correct access restrictions or hiding variables behind methods. Tests usually lie in the lower areas of your project tree and nobody should try to re-use a test object in production code. So a more pragmatic approach won't hurt your project. But if you prefer you can of course do it the clean way.
The following sections contain some pattern I already used in my tests. They are kept simple but they certainly can be extended to cover similar real world scenarios.
The following example tracks the invocations to a reader object. The caller is expected to invoke each of the reader methods exactly once in this order:
This is done by incrementing a counter and assigning the current value to one variable for each method. After the test has run the values of all variables are checked.
class ReaderInterface { public: virtual void open() = 0; virtual void read() = 0; virtual void close() = 0; }; class ReaderMock : public ReaderInterface { public: unsigned counter; unsigned open_counter; unsigned read_counter; unsigned close_counter; ReaderMock() { counter = 0; open_counter = 0; read_counter = 0; close_counter = 0; } virtual void open() { open_counter = ++counter; } virtual void read() { read_counter = ++counter; } virtual void close() { close_counter = ++counter; } }; //////////////////////////////////////////// // The test within your testing framework ReaderMock reader; myMethodUnderTest(&reader); assert(reader.counter == 3); assert(reader.open_counter == 1); assert(reader.read_counter == 2); assert(reader.close_counter == 3);
Another simple pattern is the tracking of parameters passed to a mock object. Every parameter is stored in a vector. After completion the size of the vector and each expected value is checked.
class StorageInterface { public: virtual void store(unsigned i) = 0; }; class StorageMock : public StorageInterface { public: std::vector<unsigned> params; virtual void store(unsigned i) { params.push_back(i); } }; //////////////////////////////////////////// // The test within your testing framework StorageMock store; myMethodUnderTest(&store); assert(store.params.size() == 2); assert(store.params[0] == 123); assert(store.params[1] == 456);