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 ...
答案 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;
值得注意的是,拥有用于模拟的实现文件是一种很好的做法,对于测试编译时间而言,它具有明显的好处。很好,很容易。