gpt4 book ai didi

c++ - Gtest : mocking free function in a constructor

转载 作者:行者123 更新时间:2023-11-30 04:52:02 25 4
gpt4 key购买 nike

我阅读了很多与 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 做到这一点?

最佳答案

Dependency injection (DI) 来救援!

DI 是模拟的关键插入因素。具体来说,您可以使用 Strategy pattern 将依赖项注入(inject)此对象,以便您可以在测试时将其取出。

选项 1:构造函数注入(inject)

最简单的版本是将仿函数传递给您的构造函数,并在您当前调用 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 不能很好地处理全局变量/单例变量,因为您不能(轻松地)在构建时注入(inject)依赖项,这是不鼓励它们的原因之一。

选项 2:属性注入(inject)

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

在这种情况下,您的类将如下所示:

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

static bool default_to_be_mocked( int ) { ... }

public:

H()
: _to_be_mocked( default_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 && _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,可能在这里有用。

关于c++ - Gtest : mocking free function in a constructor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54650890/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com