将 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();
}
我有遗留代码和一些基于 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();
}