如何在运行时使用 CppUTest 注入模拟类
这是一个简单的指南,教您如何将模拟类注入产品代码中,并让您的产品类在 CppUTest 单元框架下可测试。
下图是类之间的关系。ProductClassB 依赖于 ProductClassA(ProductClassB 调用 ProductClassB.method() 中的 ProductClassA.method1()),现在当我们对 ProductClassB 进行单元测试时,应该模拟 ProductClassA。

下面的代码展示了如何使用 CppUTest 使用新方法来模拟依赖的类。
ProductClassA 有一些方法,如果您希望这些方法可以被模拟,则应将这些方法声明为虚拟方法。
ProductClassA.hpp:
#ifndef _INCLUDE_PRODUCT_A_CLASS_
#define _INCLUDE_PRODUCT_A_CLASS_
#include <string>
class ProductClassA {
public:
ProductClassA() {
}
~ProductClassA() {
}
public:
virtual int method1(const std::string& arg1);
virtual int method2(const std::string& arg1);
};
#endif
ProductClassA.cpp:
#include <iostream>
#include "ProductClassA.hpp"
int ProductClassA::method1(const std::string& arg1) {
std::cout << arg1 << " ProductClassA class method1" <<std::endl;
return 0;
}
int ProductClassA::method2(const std::string& arg1) {
std::cout << arg1 << " ProductClassA class method2" <<std::endl;
return 0;
}
如果你想在对 ProductClassB 进行单元测试时模拟依赖的类 ProductClassA,则应在 ProductClassB 中声明一个具有引用类型的私有 ProductClassA 变量,并且在 ProductClassB 中可以有两个构造函数,一个用于产品代码,一个用于单元测试代码,如下所示。
ProductClassB.hpp:
#include <string>
#include "ProductClassA.hpp"
class ProductClassB {
public:
// for unit test
ProductClassB(ProductClassA& ainstance): _ainstance(ainstance) {
}
// for product code
ProductClassB(): _ainstance(ProductClassA()) {
}
~ProductClassB() {
}
public:
int method();
private:
ProductClassA& _ainstance;
};
ProductClassB.cpp:
#include "ProductClassB.hpp"
#include <iostream>
int ProductClassB::method() {
std::cout<< "this is B class method"<<std::endl;
return this->_ainstance.method1("B invoke");
}
ProductClassAMock类是ProductClassA的派生类,其中的方法将以mock的方式重新实现。
ProductClassAMock.hpp:
#include "ProductClassA.hpp"
class ProductClassAMock : public ProductClassA
{
public:
virtual int method1(const std::string& arg1);
virtual int method2(const std::string& arg1);
};
ProductClassAMock.cpp:
#include "ProductClassAMock.hpp"
#include <iostream>
#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"
int ProductClassAMock::method1(const std::string& arg1) {
std::cout << arg1 << " ProductClassAMock method1"<<std::endl;
mock().actualCall("method1");
return 0;
}
int ProductClassAMock::method2(const std::string& arg1) {
mock().actualCall("method2");
return 0;
}
ProductClassB 的单元测试将使用测试构造函数在 bInstance 中用模拟类 ProductClassAMock 初始化 _ainstance,然后根据 C++ 多态机制,将 _ainstance.method1() 替换为类 ProductClassAMock 中的 method1。
ProductClassB_unittests.cpp:
#include "ProductClassB.hpp"
#include "ProductClassAMock.hpp"
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"
TEST_GROUP(BClassFooTests)
{
void teardown()
{
mock().clear();
}
};
TEST(BClassFooTests, MockAsExpected)
{
mock().expectOneCall("method1");
ProductClassAMock aMockInstance;
ProductClassB bInstance(aMockInstance);
bInstance.method();
mock().checkExpectations();
}
int main(int ac, char** av)
{
return CommandLineTestRunner::RunAllTests(ac, av);
}
产品代码只需使用ProductClassB的产品构造函数,bInstance._ainstance将使用真实的类ProductClassA进行初始化。
main.cpp:
#include "ProductClassB.hpp"
int main() {
ProductClassB bInstance();
bInstance.method();
return 0;
}
如何构建代码:
# build main
g++ ProductClassA.cpp ProductClassB.cpp main.cpp -o main
# build unit test
g++ ProductClassA.cpp ProductClassB.cpp ProductClassAMock.cpp ProductClassB_unittests.cpp -lstdc++ -lCppUTest -lCppUTestExt -o testrunner
参考: