【问题标题】:How can I create a partial (hybrid) mock in googlemock?如何在 googlemock 中创建部分(混合)模拟?
【发布时间】:2013-05-29 04:17:21
【问题描述】:

当您需要调用真实对象的功能时,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 ...

【问题讨论】:

    标签: c++ unit-testing mocking googlemock


    【解决方案1】:

    mock 应该简单地扩展真实对象,然后默认将所有调用委托给父对象,而不是将真实对象的实例创建为成员变量。你现在可以像平常一样设置你的模拟了;设置一个新的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。它将为所有方法生成默认调用父(真实)对象的部分模拟,并生成将参数传递给父构造函数的匹配构造函数。

    【讨论】:

    • 这仍然是唯一的方法吗?不得不提所有的方法实在是太烦人了。
    • 我已经很多年没有调查过了。也许他们更新了它,或者也许还有其他更容易的框架。看起来 Google Mock 现在是 Google Test 的一部分。如有必要,您可以将我的工具移植到新框架中——它确实节省了大量输入。
    【解决方案2】:

    官方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;
    

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

    【讨论】:

    • Google Mock 指南链接已损坏。新链接:Google mock guideline
    • 谢谢@reesjones,现在已经修复了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多