gmock可以用于存根C函数吗?

时间:2015-08-13 13:02:27

标签: c unit-testing googletest gmock

我是gmock的新手,所以我想知道如何在单元测试的测试函数中调用简单的C函数。

示例:

int func(int a)
{
  boolean find;
  // Some code
  find = func_1();
  return find;
}

我搜索了gmock并且在我的理解中gmock没有提供存根简单C函数的功能,因此我想问gmock是否提供了模拟或存根func_1的功能?

如果不是,如何在不更改源代码的情况下手动在我的测试代码中存储func_1?我正在使用谷歌测试框架进行单元测试。

感谢。

6 个答案:

答案 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对象,该对象将根据状态和参数设置预期和操作。

我知道它并不完美,但它可以在不改变生产代码或界面的情况下解决问题。