1.3.4. Poor Man's Mock Objects

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.

1.3.4.1. Tracking Invocation Order

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:

  • open()
  • read()
  • close()

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

1.3.4.2. Tracking Method Parameters

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