【问题标题】:How to mock malloc to return null in GMOCK?如何在 GMOCK 中模拟 malloc 以返回 null?
【发布时间】:2013-07-15 22:06:29
【问题描述】:
我想使用 Gmock 框架在 C++ 中模拟 malloc。可能吗?我已经尝试了所有可能的方法。但是托管这个 malloc 实现的类有一个私有构造函数和析构函数?
有什么方法可以直接mock malloc 返回NULL?
【问题讨论】:
-
你可以看看DLL Injection/Library Interposing - 在 POSIX 和朋友上这工作得很好 - 有几个内存调试器依赖于这种机制 - 其中主要是 umem,以及许多依赖于此的标准分配器替换取得巨大成功的机制(nedmalloc、tcmalloc、umem、mtmalloc)。这可能比宏选项更好——因为它不需要更改现有代码——甚至不需要重新链接。
标签:
c++
mocking
googletest
googlemock
【解决方案1】:
开发者爱,
首先,模拟标准库从来都不是一个好的做法,在这种粒度级别上测试代码是为艺术而艺术。您必须注意到,从一开始,测试就成为项目的一部分,如果您想让它们保持最新(即维护工作回归),您必须以与生产代码相同的方式考虑它们的设计。事实上,测试也是在项目生命周期中必须维护的代码,如果阅读、纠正和最终理解测试将花费太多时间,那么这种回归将毫无用处。尝试将其视为“生活文档”。
尽管如此,模拟标准 C 库的最丑陋的方法之一可能是静态钩子和宏。考虑以下示例:
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <boost/bind.hpp>
#include <boost/function.hpp>
static boost::function<void*(size_t)> malloc_bridge;
struct StdlibMock
{
StdlibMock()
{
malloc_bridge = boost::bind(&StdlibMock::mallocMock, this, _1);
}
MOCK_METHOD1(mallocMock, void*(size_t));
}; // struct Struct StdlibMock
void* malloc_cheat(size_t size)
{
return malloc_bridge(size);
}
#define malloc malloc_cheat
struct Sut
{
void f()
{
malloc(10);
}
};
struct TestWithMalloc : ::testing::Test
{
StdlibMock stdlibMock;
}; // struct TestWithMalloc
TEST_F(TestWithMalloc, ShouldMalloc10Bytes)
{
EXPECT_CALL(stdlibMock, mallocMock(10))
.WillOnce(::testing::Return(static_cast<void*>(0)));
Sut sut;
sut.f();
}
#undef malloc
请注意,由于使用了预处理器宏,您不能用简单的 malloc 替换 mallocMock 函数名称。
希望对您有所帮助。
【解决方案2】:
- 包装malloc
- 将包装器传递给生产代码中的测试类 c'tor
- 模拟包装器(也可以在包装器之上创建接口并模拟它)
-
在测试代码中将 mock 传递给被测试的类 c'tor
class I_mallocWrapper
{
public:
virtual ~I_mallocWrapper() {}
virtual void* myMalloc (size_t size) = 0;
};
//wrapper to malloc
class mallocWrapper : public I_mallocWrapper
{
public:
virtual void* myMalloc (size_t size) {return malloc(size);}
virtual ~mallocWrapper() {}
mallocWrapper(){}
};
//tested class with tested method that uses the wrapper
class TestedClass
{
public:
TestedClass(I_mallocWrapper* mallocW) { this->m_mallocWrapper = mallocW; }
void testedMethod(size_t size) { m_mallocWrapper->myMalloc(size); }
virtual ~TestedClass() {}
private:
I_mallocWrapper* m_mallocWrapper;
};
//production code
void main()
{
size_t size = 18;
I_mallocWrapper* MW = new mallocWrapper;
TestedClass* TC = new TestedClass(MW);
TC->testedMethod(size);
}
//mock the wrapper
class mockMallocWrapper : public I_mallocWrapper
{
public:
MOCK_METHOD1(myMalloc, void*(size_t size));
};
//test code
TEST(MallocTest,callMalloc)
{
size_t size = 18;
I_mallocWrapper* MW = new mockMallocWrapper;
TestedClass* TC = new TestedClass(MW);
TC->testedMethod(size);
EXPECT_CALL(MW, myMalloc(_))
.WillOnce(Return(NULL))
}
【解决方案3】:
glibcmock 可以帮助你模拟 malloc 和其他 libc 函数。
#include "got_hook.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <mutex>
#include <memory>
struct MockMalloc {
MOCK_METHOD1(Malloc, void *(size_t));
};
static MockMalloc *g_mock{nullptr};
static void *Malloc(size_t size) {
return g_mock->Malloc(size);
}
static std::mutex g_test_mutex;
TEST(MallocTest, ReturnNull) {
std::lock_guard<std::mutex> lock(g_test_mutex);
std::unique_ptr<MockMalloc> mock(g_mock = new MockMalloc());
testing::GotHook got_hook;
ASSERT_NO_FATAL_FAILURE(got_hook.MockFunction("malloc", (void*)&Malloc););
// ... do your test here, for example:
EXPECT_CALL(*g_mock, Malloc(testing::_)).WillOnce(testing::Return(nullptr));
EXPECT_EQ(nullptr, malloc(1));
}