使用googlemock与非虚拟函数的假动作

时间:2016-11-03 17:54:41

标签: c++ unit-testing googletest googlemock

我有基于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的方法创建假实现。然而,在D的方法是虚拟的之前,我仍然无法使用它与所有googlemock的力量。

我喜欢使用googlemock框架中的WillOnce,AtLeast,WillRepeatedly,Invoke等功能,因为它使单元测试创​​建更容易。问题是我不喜欢改变D界面,将其方法转变为虚拟界面的想法。是否可以将googlemock函数与假实现一起使用,我将为D的方法创建?

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

1 个答案:

答案 0 :(得分:0)

首先 - 最好的方法是重新设计你的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();
}