将 googlemock 与非虚函数的假 impls 结合使用

Using googlemock with fake impls of non-virtual functions

我有遗留代码和一些基于 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 class(被测软件),其实现在文件 sut.cpp 中定义。 SUT 依赖于 D class,其实现在文件 d.cpp 中。为了减少 linker 的依赖性,我不想将 d.cpp 添加到测试中,因此当我 link 测试时,有很多针对 D 成员的 'undefined symbol' 错误。为了消除错误并提供可预测的行为,我将在我的测试中为 D 的方法创建伪造的实现。但是,在 D 的方法是虚拟的之前,我仍然无法使用 googlemock 的所有功能。

我喜欢使用 googlemock 框架中的 WillOnce、AtLeast、WillRepeatedly、Invoke 等函数的想法,因为它可以更轻松地创建单元测试。问题是我不喜欢改变 D 接口的想法,将它的方法变成虚拟方法。是否可以将 googlemock 函数 与我要为 D 的方法创建的虚假实现 一起使用?

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

首先 - 最好是重新设计您的 SUT class 以通过一些抽象接口注入 D。因为我在下面描述的解决方法真的很棘手——所以以后维护和理解起来就不那么容易了……


如果您要在 UT 目标中伪造 D class 的实现 - 那么您可以为 D: DMock 制作 Mock class。这个 DMock 不会与 D 相关 - 不是从它派生的 - 但它需要与 real/fake D 对象配对。

因此 - 请参阅示例:

创建 DMock - 模仿 D 接口(注意你应该只模拟 public 函数 - 因为你的 SUT 只使用 public 个):

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 class 的 public 方法的假实现:

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

非public方法无关紧要-所以随心所欲...

并使用 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();
}