来自外部类的模拟静态方法(我无法更改!) [英] Mock static method from external Class (that I can't change!)
问题描述
我想从我无法更改的类中模拟(使用 gmock)一个静态函数.A 是我要模拟的类:
I want to mock (with gmock) a static function from a class that I can't change. A is the class that I want to mock:
Class A
{
public:
static std::string get_id();
...
}
B 是我想用 gmock 测试的类:
B is my class that I want to test with gmock:
Class B
{
public:
B(A *a_ptr);
...
std::string foo();
private:
A *m_a_ptr;
}
B::B(A *a_ptr) : m_a_ptr(a_ptr)
{
}
std::string B::foo()
{
id = m_a_ptr->get_id();
return id;
}
如何在不改变 A 类的情况下模拟 get_id 方法?
How can I mock the method get_id without changing the class A?
推荐答案
静态依赖注入和 GMock 委托
我们将首先将您的示例最小化为以下内容(以保持后面的段落尽可能安静):
Static dependency injection and GMock delegation
We will start by minimizing your example to the following (to keep the passages that follows as non-noisy as possible):
// a.h
#include <string>
// class to mock
class A {
static std::string get_id();
};
// b.h
#include <string>
#include "a.h"
// class that use A
struct B {
std::string foo() const {
return A::get_id();
}
};
虽然你不能改变A
,但你可以把B
改为statically injection A
在产品代码中,而您可以为测试代码静态注入 A
的模拟委托:
Although you cannot change A
, you can change B
to statically inject A
in product code, whereas you can statically inject a mock delegate of A
for test code:
// b.h
#include <string>
#include "a.h"
namespace detail {
// The type template parameter is set to A by default,
// and should not need to override this default type
// in production code, but can be injected with
// mocked classes in test code.
template<typename AImpl = ::A>
struct BImpl {
std::string foo() const {
return A::get_id();
}
};
} // namespace detail
// Expose product-intent specialization.
using B = BImpl<>;
A
的模拟使用静态(非线程安全)方法来模拟对注入的静态类型的调用:
Where the mock for A
make use of static (non-thread safe) approach to mock calls to the injected static type:
// a_mock.h
#include <memory>
#include <string>
#include "gmock/gmock.h"
class AMock {
// Mocked methods.
struct Mock {
MOCK_CONST_METHOD0(get_id,
std::string());
};
// Stubbed public API for static function of object under test:
// delegates stubbed calls to the mock.
static std::string get_id() {
if (const auto mock = mock_.lock()) {
mock->get_id();
}
else {
ADD_FAILURE()
<< "Invalid mock object! The test can no "
"longer be considered useful!";
}
}
// Public setter to specify the mock instance used in test (which in
// turn will be the instance that Google Test's EXPECTS and mocked
// calls is placed upon).
static void setMock(const std::shared_ptr<Mock>& mock) { mock_ = mock; }
private:
// Pointer to mock instance.
static std::weak_ptr<Mock> mock_;
};
最终可以用于BImpl
的测试,如下所示:
which can, finally, be used in tests of BImpl
as follows:
// b_test.cpp
#include "b.h" // object under test
#include "gmock/gmock.h"
#include "a_mock.h"
class BImplTest : public ::testing::Test {
public:
using BImplUnderTest = BImpl<AMock>;
BImplTest() : amock_(std::make_shared<AMock::Mock>()) {
AMock::setMock(amock_);
}
};
TEST_F(BImplTest, foo) {
// Setup mocked call(s).
EXPECT_CALL(amock_, foo()).WillOnce(::testing::Return( /*...*/ ));
// Call object under test.
BImplUnderTest b{};
b.foo();
}
进一步隐藏B
实际上是类模板的特化BImpl
如果您开始大量使用此模式(在不同子例程上以滑动窗口方式)并希望避免单个大而臃肿的翻译单元,您可以移动 的成员函数的定义细节::B
类模板来分隔标头,例如 b-timpl.h
(包括 bh
)和与 bh<关联的源文件/code>,比如
专业化:b.cpp
,包含 b-timpl.h
而不是 bh
并为生产意图添加显式实例化定义 <代码>detail::BImpl
Further hiding the fact that B
is in fact a specialization of a class template BImpl
If you start to heavily use this pattern (in a sliding-window manner over different sub-routines) and want to avoid single large and bloated translation units, you could move the definitions of the member functions of the detail::B
class template to separate header, say b-timpl.h
(which includes b.h
) and in the source file associated with b.h
, say b.cpp
, include b-timpl.h
instead of b.h
and add an explicit instantiation definition for the production intent detail::BImpl
specialization:
// b.cpp
template class ::detail::BImpl<>;
而在 ::detail::BImpl
的测试中,您包含 b-timpl.h
而不是 bh
并添加显式实例化定义对于类模板的模拟注入特化:
Whereas in tests of ::detail::BImpl
you include b-timpl.h
instead of b.h
and add an explicit instantiation definition for the mock-injected specialization of the class template:
// b_test.cpp
#include "b-timpl.h"
// ...
template class ::detail::BImpl<AMock>;
// ...
为什么?BImpl
类没有参数化以允许其界面的用户静态注入不同的行为(对于用户意图,用户应该只看到 B
),但允许注入模拟或测试时存根类.
Why? The BImpl
class is not parameterized to allow a user of its interface to statically inject different behahaviour (for user intent, users should only see B
), but to allow injecting mocked or stubbed classes while testing.
这篇关于来自外部类的模拟静态方法(我无法更改!)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!