2016-11-03 79 views
1

我有一些基于googlemock框架的单元测试的遗留代码。当我试图单元测试一些新的情况下延长我遇到了以下问题:使用googlemock与假虚拟impls的非虚函数

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类的解决方案,但是我想知道是否存在其他解决方案。

回答

0

首先 - 最好的是重新设计你的SUT类,使其通过一些抽象接口进行D注入。因为我下面介绍的解决方法是真正棘手 - 所以不那么容易维护和未来的了解...


如果你想使你的d类的假implememtation在UT目标 - 那么你就可以为D制作Mock类:DMock。这DMock将不会与D - 不是从它派生 - 但它需要与真正/假的D对象配对。

所以 - 看到的例子:

创建DMock - 模仿的d接口(请注意,您应该嘲笑只有公共职能 - 因为你的SUT只使用公共的):

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

配对与DMock对象实(但假的)d的对象 - 是这样的:

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; 
}; 

使用模拟,以制造假执行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(); 
} 
+0

谈到通过一个抽象的注入d接口。我认为我有某种恐惧症,但似乎有一个抽象界面与唯一的派生类似乎很奇怪。另一方面,我明白这种方法在单元测试中的优点。所以我不能决定使用这种方法)) – Rom098

+1

用这种方式来思考这个问题:代码应该具有实现的功能 - >唯一的证明功能在于它被测试 - >可测试性很重要 - >应以可测试的方式设计实施。因此,添加一些“只是为了测试”是没有错的... – PiotrNycz

+0

听起来不错))我接受了你的答案,既好的建议和技巧。 – Rom098