Monday, February 25, 2013

Piotr's Less Obvious Advice on Google Mock: Mutual method calls

Imagine that object A calls method M of object B and B is a mock object. Let's assume further that if B were real, calling the M method would result in B calling a method on A (M would call an A's method from its body). Suppose we want to simulate the same behavior with a mock object. We have a "master" and "slave" objects called Starter and Startee, respectively.

class Starter
{
 public:
  Starter(Startee & startee, int vol);
  virtual ~Starter() {}

  void start();
  void configure();

 private:
  Startee & startee_;
  int vol_;

  Starter();
};

// implementation of the above Starter methods
Starter::Starter(Startee & startee, int vol) : startee_(startee), vol_(vol)
{
}

// Starter will call run method on startee
void Starter::start()
{
  startee_.run();
}

// this method is supposed to be called by Startee as a result of calling startee_.run()
void Starter::configure()
{
  startee_.setVolume(vol_);
}

And now, Startee:

class Startee
{
 public:

  virtual ~Startee() {}

  virtual void run() = 0;
  virtual void setVolume(int volume) = 0;
};

And MockStartee:

class MockStartee : public Startee
{
 public:

  MOCK_METHOD0(run, void());
  MOCK_METHOD1(setVolume, void(int volume));
};

In the test, we will need to tell the mock object to call configure method once the Starter object calls run method on MockStartee:
TEST(StarterTest, MutualCall)
{
  MockStartee startee;
  Starter starter(startee, 10);
 
  EXPECT_CALL(startee, setVolume(10))
    .Times(1)
    .WillOnce(Return());
 
  EXPECT_CALL(startee, run())
    .Times(1)
    .WillOnce(WithoutArgs(Invoke(&starter, &Starter::configure)));

  starter.start();
}

See also