[英]How can I create a partial (hybrid) mock in googlemock?
當您需要調用真實對象的功能時,Google 建議將調用委托給父對象,但這並不會真正創建部分(混合)模擬。 調用真實對象時,任何方法調用都是真實對象的方法調用,而不是模擬對象,您可能已經在其上設置了操作/期望。 如何創建僅將特定方法委托給真實對象的部分模擬,並將所有其他方法調用委托給模擬對象?
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 ...
模擬應該簡單地擴展真實對象,而不是創建真實對象的實例作為成員變量,然后默認情況下將所有調用委托給父對象。 您現在可以像往常一樣設置模擬; 設置新的ON_CALL
將覆蓋對父級的默認調用。 我們讓多態為我們完成工作——所有調用,甚至來自父(真實)對象的調用,都會調用模擬對象,然后將ON_CALL
語句設置為調用父對象或模擬行為。 我們已經成功地將真實對象行為與模擬行為混合在一起。 這與將調用委托給父類完全相同。
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 來簡化這個過程; 代碼可在此處獲得。 它將生成默認情況下為所有方法調用父(真實)對象的部分模擬,並生成將參數傳遞給父構造函數的匹配構造函數。
官方的Google Mock 指南和最后一個提案確實有效,但是引入了很多樣板代碼。
所以這是我的建議:
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;
值得注意的是,擁有模擬的實現文件是一種很好的做法,對測試編譯時間有明顯的好處。 好,易於。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.