Gtest:在构造函数中模拟自由函数

问题描述 投票:1回答:1

我阅读了很多与Gtest模拟相关的文档(例如,https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md,“Mocking Free Functions”),但找不到以下问题的解决方案:

source.cpp

H::H()
{
    // some code1
    if (to_be_mocked(id) != 0) { // some code2 }
    // some code3
}

H& H::get_instance()
{
    static H s;
    return s;
}

unit_test.cpp

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "source.h"

TEST(Source, Constructor)
{
    // What to write here to mock function "to_be_mocked"?
    H& inst = H::get_instance();
}

int main(int argc, char** argv)
{
  testing::InitGoogleMock(&argc, argv);
  return RUN_ALL_TESTS();
}

因此,我需要在H的构造函数和模拟函数to_be_mocked中测试整个代码,该函数在不同的转换单元中定义。我怎么能从unit_test.cpp做到这一点?

c++ unit-testing c++11 mocking googletest
1个回答
1
投票

Dependency injection(DI)救援!

DI是嘲弄的关键推动因素。具体来说,您可以使用Strategy pattern将依赖项注入此对象,以便您可以在测试时将其分出。

选项1:构造函数注入

最简单的版本是将一个仿函数传递给构造函数,并调用当前调用to_be_mocked()的位置。

在这种情况下,您的类看起来像:

class H 
{
    std::function<bool(int)> _to_be_mocked;
public:
    H( std::function<bool(int)> fn ) 
        : _to_be_mocked( std::move(fn) ) 
    {
        uses_mockable( 42 ); 
    }

    void uses_mockable( int id ) 
    {
        if( _to_be_mocked(id) ) { ... }
    }

    ...
};

void MyTest() 
{
    auto my_mock_fn = ...;
    auto h = H{ my_mock_fn };

    // Set expectations to assert that my_mock_fn is used correctly
    // and that the caller behaves properly in response to its return values
}

DI不适合全局/单身,因为你不能(轻松地)在施工时注入依赖关系,这也是他们气馁的原因之一。

选项2:财产注入

如果您不能将单例更改为常规实例或单独控制其初始构造,您可以在其中注入依赖项,那么您可以使用基于属性的注入来公开(或选择性地通过类似Attorney-Client idiom)然后公开函数在需要时设置它。

在这种情况下,您的类看起来像:

class H 
{
    std::function<bool(int)> _to_be_mocked;
public:

    H() 
        : _to_be_mocked( to_be_mocked ) // Use stand-alone function for default
    { /* can't use mock here */ }

    // Could restrict accessibility here with Attorney-Client idiom or friendship
    void set_to_be_mocked( std::function<bool(int)> fn ) 
    { 
        _to_be_mocked = std::move( fn ); 
    }

    void uses_mockable( int id ) 
    {
        if( _to_be_mocked( id ) ) { ... }
    }

    ...
};

void MyTest() 
{
    auto my_mock_fn = ...;
    auto& h = H::get_instance();
    // ...
    h.set_to_be_mocked( my_mock_fn );

    // Set expectations to assert that my_mock_fn is used correctly
    // and that the caller behaves properly in response to its return values
}

如果你需要在构造函数中调用_to_be_mocked(),这种方法将不起作用,所以你必须采用前一种方法(首选)或者使用选项3。

选项3:全球变量的穷人DI

如果你不能使用上述任何一种方法,你可以使用Yet Another Global来使用“穷人的DI”(他们说,罪会产生罪恶)。在不更改调用代码的情况下执行此操作的一种方法是将to_be_mocked()重命名为to_be_mocked_impl(),并创建名为to_be_mocked的全局函子:

bool to_be_mocked_impl( int id ) { ... } // used to be called to_be_mocked(int)

// Global instance that can be swapped out for testing.
// Defaults to the normal runtime function.
// Might use a raw function pointer instead of a std::function if you prefer.
auto to_be_mocked = std::function<bool(int)>{ to_be_mocked_impl };

class H 
{
public:
    H() 
    {
        uses_mockable( 42 ); 
    }

    void uses_mockable( int id ) 
    {
        if( to_be_mocked(id) ) { ... }
    }

    ...
};

void MyTest() 
{
    auto my_mock_fn = ...;
    to_be_mocked = my_mock_fn; // Sets global ... blah!!

    auto& h = H::get_instance();

    // Set expectations to assert that my_mock_fn is used correctly
    // and that the caller behaves properly in response to its return values
}

这会使全局/单身人士的情况更加复杂,所以除非你出于不合理的原因,否则我不会推荐它。

PS,有一个episode of CppCast on Dependency Injection谈到可能在这里有用的实验性[Boost.]DI library

© www.soinside.com 2019 - 2024. All rights reserved.