使用googlemock模拟非虚拟函数

Using googlemock with fake impls of non-virtual functions

本文关键字:虚拟 函数 模拟 googlemock 使用      更新时间:2023-10-16

我有一些基于googlemock框架的单元测试遗留代码。当我试图用一些新的场景扩展单元测试时,我遇到了以下问题:

class D
{
public:
  void pubMethod1();
  int pubMethod2();
  // There are pretty much non-virtual methods, both public and private
  ...
protected:
  uint method3();
  void method4();
  ...
  // Some class members are here
};
class SUT
{
public:
  ...
protected:
   D _dep;
};

应该测试SUT类(待测软件),其实现在文件sut.cpp中定义。SUT依赖于D类,其实现在文件d.cpp中。为了减少链接器依赖关系,我不想将d.cpp添加到测试中,因此当我链接测试时,对D的成员有许多"未定义符号"错误。为了消除错误并提供可预测的行为,我将在测试中为D的方法创建假实现。然而,我仍然不能使用它与所有的googlemock的力量,直到D的方法是虚拟的。

我喜欢使用来自googlemock框架的WillOnce, AtLeast, willrepeat, Invoke等函数,因为它使单元测试创建更容易。问题是我不喜欢改变D的接口,把它的方法变成虚拟方法的想法。是否有可能使用googlemock函数与假实现我要为D的方法创建?

注意:我已经考虑了模板SUT类的解决方案,但是我想知道是否存在其他解决方案。

首先,最好是重新设计SUT类,让D通过一些抽象接口注入。因为我下面描述的解决方法真的很棘手——所以在将来不太容易维护和理解……


如果你打算在UT目标中实现你的D类,那么你可以为D: DMock制作Mock类。这个DMockD没有关系——不是从它派生出来的——但它需要与真实/虚假的D对象配对。

所以-看这个例子:

创建DMock——模仿D接口(注意您应该只模仿公共函数——因为您的SUT只使用公共函数):

class DMock 
{
public:
    MOCK_METHOD0(pubMethod1, void ());
    MOCK_METHOD0(pubMethod2, int ());
};

将真实的(但假的)D对象与DMock对象配对-像这样:

class DMockRepo
{
public:
    // for UT
    void addMockToUse(DMock* dMock) { freeMock.push_back(dMock); }
    // for implementing D fake methods
    DMock& getMock(D* original)
    {
        // TODO: use more sophisticated way to add mock to map...
        if (not usedMock[original])
        {
           usedMock[original] = freeMock.front();
           freeMock.pop_front();
        }
        return *useddMock[original];
    }
    static DMockRepo& getInstance() { return instance; } //singleton
private:
    DMockRepo() {} // private
    static DMockRepo instance;
    std::map<D*,DMock*> usedMock;
    std::deque<DMock*> freeMock; 
};

使用mock创建D类的公共方法的伪实现:

void D::pubMethod1()
{
    DMockRepo::getInstance().getMock(this).pubMethod1();
} 
// 

非公共方法是不相关的-所以做你喜欢的…

并使用DMockRepo来设置D对象的期望:

TEST(Usage,Example)
{
   DMock dMock;
   DMockRepo::getInstance().addMockToUse(&dMock);
   SUT sut; // you should know how many D objects SUT needs - I assume just one
   EXPECT_CALL(dMock, pubMethod1());
   sut.doSomethingThatCallsDpubMethod1();
}