来自外部类的模拟静态方法(我无法更改!) [英] Mock static method from external Class (that I can't change!)

查看:28
本文介绍了来自外部类的模拟静态方法(我无法更改!)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从我无法更改的类中模拟(使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆