我是gmock的新手,所以我想知道如何在单元测试的测试函数中调用简单的C函数。
示例:
int func(int a)
{
boolean find;
// Some code
find = func_1();
return find;
}
我搜索了gmock并且在我的理解中gmock没有提供存根简单C函数的功能,因此我想问gmock是否提供了模拟或存根func_1
的功能?
如果不是,如何在不更改源代码的情况下手动在我的测试代码中存储func_1
?我正在使用谷歌测试框架进行单元测试。
感谢。
答案 0 :(得分:14)
我最近发现自己处于同样的境地。我不得不写单元测试 用C语言编写的库,后者又依赖于其他用C语言编写的库。所以我想模拟依赖项的所有函数调用 使用 gmock 。让我通过一个例子解释我的方法。
假设要测试的代码(库A)从另一个库lib_x_function()
调用一个函数:
lib_a_function()
{
...
retval = lib_x_function();
...
}
所以,我想模拟库X.因此我编写了一个接口类和一个
文件lib_x_mock.h
中的模拟类:
class LibXInterface {
public:
virtual ~LibXInterface() {}
virtual int lib_x_function() = 0;
}
class LibXMock : public LibXInterface {
public:
virtual ~LibXMock() {}
MOCK_METHOD0(lib_x_function, int());
}
另外,我创建了一个定义存根的源文件(比如lib_x_mock.cc
)
对于实际的C函数。这将调用mock方法。请注意extern
对模拟对象的引用。
#include lib_x.h
#include lib_x_mock.h
extern LibXMock LibXMockObj; /* This is just a declaration! The actual
mock obj must be defined globally in your
test file. */
int lib_x_function()
{
return LibXMockObj.lib_x_function();
}
现在,在测试库A的测试文件中,我必须定义模拟对象
全局,以便它在您的测试中都可以访问
lib_x_mock.cc
。这是lib_a_tests.cc:
#include lib_x_mock.h
LibXMock LibXMockObj; /* This is now the actual definition of the mock obj */
...
TEST_F(foo, bar)
{
EXPECT_CALL(LibXMockObj, lib_x_function());
...
}
这种方法对我来说非常有效,而且我有几十个测试和几个 嘲笑图书馆。但是,我有一些疑问是否可以创建一个 全局模拟对象 - 我在separate question中问了这个问题并且仍在等待答案。除此之外,我对解决方案感到满意。
编辑:通过创建对象,可以轻松解决有关全局对象的问题。在测试夹具的构造函数中,只是在全局变量中存储指向该对象的指针。
但是,请注意我刚才发布的这个问题的替代答案。
答案 1 :(得分:6)
这是我对这个问题的另一个答案。在第一个回答后的两年中,我开始明白GMock只是模拟C函数的错误框架。在你有很多函数需要模拟的情况下,我以前发布的答案太麻烦了。原因是GMock使用 Object Seams 将生产代码替换为模拟代码。这依赖于多态类,它们不存在于C中。
相反,要模拟C函数,您应该使用 Link Seams ,它在链接时用模拟代码替换生产代码。为此目的存在几个框架,但我最喜欢的是假功能框架(FFF)。看看它,它比GMock简单得多。它在C ++应用程序中也能很好地工作。
对于感兴趣的人,Michael Feathers关于不同的接缝类型是good article。
答案 2 :(得分:5)
我在寻找使用googleMock模拟遗留c函数的解决方案时已经很长时间了,而且没有更改现有代码,最后几天我发现了以下非常棒的文章:https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb
今天我使用gmock编写了我的第一个c函数单元测试,并以bcm2835.c库(http://www.airspayce.com/mikem/bcm2835/)中的两个函数为例进行了覆盆子Pi编程: 这是我的解决方案:我正在使用gcc 4.8.3。在Eclipse和Windows下。请注意设置编译器选项-std = gnu ++ 11。
以下是我要测试的功能
int inits(void);
void pinMode(uint8_t pin, uint8_t mode);
int inits(){
return bcm2835_init();
}
void pinMode(uint8_t pin, uint8_t mode){
bcm2835_gpio_fsel(pin, mode);
}
使用googleTest / googleMock包含和定义单元测试
// MOCKING C-Functions with GMOCK :)
#include <memory>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
using namespace ::testing;
using ::testing::Return;
模拟BCM2835Lib函数
class BCM2835Lib_MOCK{
public:
virtual ~BCM2835Lib_MOCK(){}
// mock methods
MOCK_METHOD0(bcm2835_init,int());
MOCK_METHOD2(bcm2835_gpio_fsel,void(uint8_t,uint8_t));
};
创建一个TestFixture
class TestFixture: public ::testing::Test{
public:
TestFixture(){
_bcm2835libMock.reset(new ::testing::NiceMock<BCM2835Lib_MOCK>());
}
~TestFixture(){
_bcm2835libMock.reset();
}
virtual void SetUp(){}
virtual void TearDown(){}
// pointer for accessing mocked library
static std::unique_ptr<BCM2835Lib_MOCK> _bcm2835libMock;
};
实例化模拟的lib函数
// instantiate mocked lib
std::unique_ptr<BCM2835Lib_MOCK> TestFixture::_bcm2835libMock;
假lib功能将Mocks与c-functions连接
// fake lib functions
int bcm2835_init(){return TestFixture::_bcm2835libMock->bcm2835_init();}
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode){TestFixture::_bcm2835libMock->bcm2835_gpio_fsel(pin,mode);}
从TestFixture创建BCM2835的单元测试类
// create unit testing class for BCM2835 from TestFixture
class BCM2835LibUnitTest : public TestFixture{
public:
BCM2835LibUnitTest(){
// here you can put some initializations
}
};
使用googleTest和googleMock
编写测试TEST_F(BCM2835LibUnitTest,inits){
EXPECT_CALL(*_bcm2835libMock,bcm2835_init()).Times(1).WillOnce(Return(1));
EXPECT_EQ(1,inits()) << "init must return 1";
}
TEST_F(BCM2835LibUnitTest,pinModeTest){
EXPECT_CALL(*_bcm2835libMock,bcm2835_gpio_fsel( (uint8_t)RPI_V2_GPIO_P1_18
,(uint8_t)BCM2835_GPIO_FSEL_OUTP
)
)
.Times(1)
;
pinMode((uint8_t)RPI_V2_GPIO_P1_18,(uint8_t)BCM2835_GPIO_FSEL_OUTP);
}
结果:)
[----------] 2 tests from BCM2835LibUnitTest
[ RUN ] BCM2835LibUnitTest.inits
[ OK ] BCM2835LibUnitTest.inits (0 ms)
[ RUN ] BCM2835LibUnitTest.pinModeTest
[ OK ] BCM2835LibUnitTest.pinModeTest (0 ms)
[----------] 2 tests from BCM2835LibUnitTest (0 ms total)
希望它会有所帮助:) - 对我而言,这是一个非常有效的解决方案。
答案 3 :(得分:1)
您可以使用Cutie库来模拟C函数GoogleMock样式。
回购中有完整的样本,但只是一种味道:
INSTALL_MOCK(close);
CUTIE_EXPECT_CALL(fclose, _).WillOnce(Return(i));
答案 4 :(得分:0)
在每个UT中,我们都在尝试验证特定行为。
当你很难/不可能(我们需要隔离我们的单位)/花费大量时间(运行时间......)模拟特定行为时,你应该假装。
以显式方式使用'C'功能意味着该功能是您单位的一部分(因此您不应该模仿它...)。在this回答中,我解释了按原样测试方法的举措(在编辑中)。在我看来,您应该使用导致func
模拟您要验证的行为的参数调用func_1
。
GMock
基于编译假(宏),因此你不能做这样的事情。要伪造'C'方法,您必须使用其他工具,例如Typemock Isolator++。
如果您不想使用Isolator++
,那么您应该重构您的方法;将func
更改为func(int a, <your pointer the function>)
,然后使用指针代替func_1
。
this答案中的我的图表可能有助于决定处理案件的方式。
答案 5 :(得分:0)
我在一个项目中有一个类似的案例,我正在进行单元测试。我的解决方案是创建两个make文件,一个用于生产,一个用于测试。
如果函数func_1()在头文件a.h中定义,并在a.cpp中实现,那么为了测试,你可以添加一个新的源文件a_testing.cpp,它将实现a.h中的所有函数作为存根。 对于单元测试,只需编译并链接a_testing.cpp而不是a.cpp,经过测试的代码将调用你的存根。
在a_testing.cpp中,您可以将调用转发给gmock对象,该对象将根据状态和参数设置预期和操作。
我知道它并不完美,但它可以在不改变生产代码或界面的情况下解决问题。