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

use*_*610 12 c unit-testing googletest gmock

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

例:

int func(int a)
{
  boolean find;
  // Some code
  find = func_1();
  return find;
}
Run Code Online (Sandbox Code Playgroud)

我搜索过gmock,在我的理解中gmock没有提供存根简单C函数的功能,因此我想问一下gmock是否提供了mock或stub的功能func_1

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

谢谢.

Geo*_* P. 16

我最近发现自己处于同样的境地.我不得不为用C编写的库编写单元测试,而这些库又依赖于其他用C语言编写的库.所以我想用gmock模拟所有依赖项的函数调用.让我通过一个例子解释我的方法.

假设要测试的代码(库A)从另一个库调用函数,lib_x_function():

lib_a_function()
{
   ...
   retval = lib_x_function();
   ...
}
Run Code Online (Sandbox Code Playgroud)

所以,我想模拟库X.因此我在一个文件中编写了一个接口类和一个mock类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());
}
Run Code Online (Sandbox Code Playgroud)

另外,我创建了一个源文件(比方说lib_x_mock.cc),它定义了实际C函数的存根.这将调用mock方法.请注意对extern mock对象的引用.

#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();
}
Run Code Online (Sandbox Code Playgroud)

现在,在测试库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());
   ...
}
Run Code Online (Sandbox Code Playgroud)

这种方法对我来说非常有效,我有几十个测试和几个模拟库.但是,我有一些疑问是否可以创建一个全局模拟对象 - 我在一个单独的问题中问这个并仍在等待答案.除此之外,我对解决方案感到满意.


编辑:关于全局对象的问题可以通过在测试夹具的构造函数中创建对象,并且只是在全局变量中存储指向该对象的指针来轻松解决.

但是,请注意我刚才发布的这个问题的替代答案.


Geo*_* P. 7

这是我对这个问题的另一个答案.在第一个回答后的两年中,我开始明白GMock只是模拟C函数的错误框架.在你有很多函数需要模拟的情况下,我以前发布的答案太麻烦了.原因是GMock使用Object Seams 用模拟代码替换生产代码.这依赖于C中不存在的多态类.

相反,要模拟C函数,您应该使用链接接缝,它在链接时用模拟代码替换生产代码.为此目的存在几个框架,但我最喜欢的是假功能框架(FFF).看看它,它比GMock简单得多.它在C++应用程序中也能很好地工作.

对于感兴趣的人来说,这是Michael Feathers关于不同接缝类型的好文章.


小智 5

我已经花了很长时间寻找使用googleMock模拟遗留c函数的解决方案,而不更改现有代码,最后几天我发现了以下非常好的文章:https://www.codeproject.com/articles/1040972/using-googletest -and-googlemock的框架换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);
}
Run Code Online (Sandbox Code Playgroud)

使用googleTest/googleMock包含和定义单元测试

// MOCKING C-Functions with GMOCK :)
#include <memory>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
using namespace ::testing;
using ::testing::Return;
Run Code Online (Sandbox Code Playgroud)

模拟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));
};
Run Code Online (Sandbox Code Playgroud)

创建一个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;
};
Run Code Online (Sandbox Code Playgroud)

实例化模拟的lib函数

// instantiate mocked lib
std::unique_ptr<BCM2835Lib_MOCK> TestFixture::_bcm2835libMock;
Run Code Online (Sandbox Code Playgroud)

假lib函数用于将Mocks与c函数连接起来

// 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);}
Run Code Online (Sandbox Code Playgroud)

从TestFixture为BCM2835创建单元测试类

// create unit testing class for BCM2835 from TestFixture
class BCM2835LibUnitTest : public TestFixture{
public:
    BCM2835LibUnitTest(){
        // here you can put some initializations
    }
};
Run Code Online (Sandbox Code Playgroud)

使用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);
}
Run Code Online (Sandbox Code Playgroud)

结果:)

[----------] 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)
Run Code Online (Sandbox Code Playgroud)

希望它会有所帮助:) - 对我而言,这是一个非常有效的解决方案.

  • 您为"实例化模拟lib函数"显示的代码在哪里生效?哪个.cpp文件是? (2认同)

Old*_*Fox 0

在每个 UT 中,我们都试图验证特定的行为。

当它非常困难/不可能(我们需要隔离我们的单元)/花费大量时间(运行时间..)来模拟特定行为时,你应该伪造一些东西。

以显式方式使用“C”函数意味着该函数是您的单元的一部分(因此您不应该嘲笑它......)。在这个答案中,我解释了按原样测试该方法的举措(在编辑中..)。在我看来,您应该使用导致模拟您想要验证的行为的func参数进行调用。func_1

GMock基于编译假(宏),因此你不能做这样的事情。要伪造“C”方法,您必须使用不同的工具,例如Typemock Isolator++

如果你不想使用Isolator++,那么你应该重构你的方法;更改funcfunc(int a, <your pointer the function>),然后使用指针代替func_1

我在这个答案中的图表可能有助于决定处理您的案件的方式。