How can I create a partial (hybrid) mock in googlemock?
Asked Answered
I

2

7

Google suggests delegating calls to a parent object when you need to invoke functionality of the real object, however this does not really create a partial (hybrid) mock. When invoking the real object, any method calls are those of the real object and not the mock object, on which you may have set up actions/expectations. How do I create a partial mock that delegates only specific methods to the real object, and all other method calls to the mock object?

Delegate to real object example

using ::testing::_;
using ::testing::AtLeast;
using ::testing::Invoke;

class MockFoo : public Foo {
 public:
  MockFoo() {
    // By default, all calls are delegated to the real object.
    ON_CALL(*this, DoThis())
        .WillByDefault(Invoke(&real_, &Foo::DoThis));
    ON_CALL(*this, DoThat(_))
        .WillByDefault(Invoke(&real_, &Foo::DoThat));
    ...
  }
  MOCK_METHOD0(DoThis, ...);
  MOCK_METHOD1(DoThat, ...);
  ...
 private:
  Foo real_;
};
...

  MockFoo mock;

  EXPECT_CALL(mock, DoThis())
      .Times(3);
  EXPECT_CALL(mock, DoThat("Hi"))
      .Times(AtLeast(1));
  ... use mock in test ...
Iron answered 3/6, 2013 at 3:7 Comment(0)
I
7

Instead of creating an instance of the real object as a member variable, the mock should simply extend the real object, then delegate all calls to the parent by default. You can now setup your mock like normal; setting a new ON_CALL will override the default call to the parent. We let polymorphism do the work for us -- all calls, even from the parent (real) object, invoke the mock object, then the ON_CALL statement was set to invoke either the parent object or the mock behavior. We have successfully mixed real object behavior with mock behavior. This is exactly the same as delegating calls to a parent class.

Delegate to parent class example

class Foo {
 public:
  virtual ~Foo();

  virtual void Pure(int n) = 0;
  virtual int Concrete(const char* str) { ... }
};

class MockFoo : public Foo {
 public:
  // Mocking a pure method.
  MOCK_METHOD1(Pure, void(int n));
  // Mocking a concrete method.  Foo::Concrete() is shadowed.
  MOCK_METHOD1(Concrete, int(const char* str));

  // Use this to call Concrete() defined in Foo.
  int FooConcrete(const char* str) { return Foo::Concrete(str); }
};

using ::testing::Invoke;
// Create mock instance foo.
...
// Delegate to parent.
ON_CALL(foo, Concrete(_))
    .WillByDefault(Invoke(&foo, &MockFoo::FooConcrete));

The only downside to this technique is that it requires a lot of boilerplate code and is sensitive to code changes. I have extended googlemock to ease this process; the code is available here. It will generate partial mocks that call the parent (real) object by default for all methods, and generate matching constructors that pass arguments to the parent constructor.

Iron answered 3/6, 2013 at 3:8 Comment(2)
Is this still the only way? It's so annoying to have to mention all methods.Schaal
I have not investigated this in years. Perhaps they updated it or maybe there are other frameworks where it is easier. It looks like Google Mock is now part of Google Test. If necessary, you can port my tool into the new framework -- it really did save a lot of typing.Iron
N
2

The official Google Mock guideline and also the last proposal do work however introduce a lot of boilerplate code.

So here is my proposal:

Foo.h

class Foo {
 public:
  virtual ~Foo();

  virtual void Pure(int n) = 0;
  virtual int Concrete(const char* str) { ... }
};

MockFoo.h

class MockFoo: public Foo {
 using Real = Foo;
 public:
  MockFoo();
  virtual ~MockFoo();

  MOCK_METHOD1(Pure, void(int n));
  MOCK_METHOD1(Concrete, int(const char* str));
};

MockFoo.cpp

MockFoo::MockFoo() {
 using ::testing::Invoke;
 ON_CALL(*this, Pure()).WillByDefault(Invoke([this] {return Real::Pure();}));
 ON_CALL(*this, Concrete()).WillByDefault(Invoke([this] {return Real::Concrete();}));
};

MockFoo::~MockFoo() = default;

It's worth noting that having an implementation file for the mock is a good practice with observable benefits for test compilation time. Nice and easy.

Nombles answered 13/5, 2020 at 19:29 Comment(2)
Google Mock guideline link is broken. New link: Google mock guidelineWhipstitch
Thanks @reesjones, it’s fixed now.Nombles

© 2022 - 2024 — McMap. All rights reserved.