如何在googlemock中创建部分(混合)模拟?

时间:2013-06-03 03:07:32

标签: c++ unit-testing mocking googlemock

当您需要调用真实对象的功能时,Google建议delegating calls to a parent object,但这并不能真正创建部分(混合)模拟。在调用真实对象时,任何方法调用都是真实对象的调用,而不是模拟对象,您可能已经设置了操作/期望。如何创建仅将特定方法委托给真实对象的部分模拟,以及对模拟对象的所有其他方法调用?

委托实际对象示例

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 ...

2 个答案:

答案 0 :(得分:4)

模拟应该只是扩展真实对象,然后默认将所有调用委托给父对象,而不是将真实对象的实例创建为成员变量。您现在可以像平常一样设置模拟;设置新的ON_CALL将覆盖对父级的默认调用。我们让多态为我们工作 - 所有调用,甚至从父(真实)对象调用模拟对象,然后ON_CALL语句被设置为调用父对象或模拟行为。我们已成功地将实际对象行为与模拟行为混合在一这与delegating calls to a parent class完全相同。

委托父类示例

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

这种技术的唯一缺点是它需要大量样板代码并且对代码更改很敏感。我已经扩展了googlemock来简化这个过程;代码是available here。它将生成部分模拟,默认情况下为所有方法调用父(实际)对象,并生成将参数传递给父构造函数的匹配构造函数。

答案 1 :(得分:1)

Google Mock guideline官方以及最后的提案都可以使用,但是引入了许多样板代码。

这是我的建议:

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;

值得注意的是,拥有用于模拟的实现文件是一种很好的做法,对于测试编译时间而言,它具有明显的好处。很好,很容易。