学习实践:使用模式,原则实现一个C++自动化测试程序

本文探讨了在C++编程中如何通过重构提升代码质量,同时利用自动化测试确保重构后功能的一致性。指出重构时保持接口稳定能降低自动化测试的工作量,并建议将单元测试中的异常情况转化为测试用例,以实现更高效的测试流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

个人编程中比较喜欢重构,重构能够提高自己的代码质量,使代码阅读起来也更清晰。

但是重构有一个问题,就是如何保证重构后带代码实现的功能与重构前的一致;如果每次重构完成后,对此不闻不问,则会有极大的风险,如果每次重构后,都进行一边测试,则工作量会很巨大,最终可能是即使代码有重构的欲望,也会尽量克制住,不去重构。除非代码能够进行自动化测试。

实际上进行测试的是接口,而不是所有代码,只要能够保持接口不变,自动化测试的工作量也没有想象中的巨大。其实我们在单元测试的时候,会测试各种异常情况,只不过,没有将这些测试写成测试代码罢了。

在Java中有JUnit,在C#中有NUnit,在C++中,笔者并不知道有哪些自动化测试工具(笔者的孤陋寡闻)。于是就产生了自己写一个自动化测试程序的想法。
自动化测试程序本质上应该是一个Command模式的应用案例,将多个Command对象保存起来,这些Command对象中存储着测试函数,在需要的时候,运行这些Command对象,并根据这些Command对象的执行结果判断测试是否通过。
首先就是定义Command对象。Command对象比较简单,定义如下:
   
   
   
typedef std::function<bool(TestInfo&)> TestFun;
这是一个返回值为布尔类型,输入参数为TestInfo引用的函数对象,如果返回值返回true表示测试通过,返回false表示测试未通过。
TestInfo也不复杂,它主要包含本次测试的一些信息,包括测试属性和测试结果等。测试属性有这个测试的名称,一些描述以及是否期望抛出异常等,测试结果就是测试是否成功。TestInfo代码如下:
  
  
  
/**
* @brief 测试信息对象,保存测试信息及测试结果。
*
*/
class TestInfo
{
public:
TestInfo()
{
level = 1;
name = "";
subName = "";
isOK = false;
isWantException = false;
remark = "";
}
public:
int level; /**< 测试用例级别 */
std::string name; /**< 测试接口名称 */
std::string subName; /**< 测试接口名称具体描述 */
bool isOK; /**< 测试结果 */
bool isWantException; /**< 是否期望异常发生 */
std::string remark; /**< 备注信息 */
};

有了Command对象,还需要有存储及运行Command对象的地方,因此我添加了一个叫TestBaseEX的类,之所以叫Ex,是因为我以前实现过一个 TestBase的类,后来在 TestBase的基础上做了改进,名称编程了 TestBaseEx。
TestBaseEX中,将Command对象存储在一个vector中,并提供了一个OnTest方法来运行这些Command对象。

    
    
    
/**
* @brief 测试基础类。
*
*/
class TestBaseEX
{
public:
typedef std::function<bool(TestInfo&)> TestFun;
/**
* @brief 执行测试。
* @param[in] testShow 测试结果展示函数
*
*/
void OnTest(std::function<void(TestInfo&)> testShow)
{
for (auto it = m_Tests.begin(); it != m_Tests.end();
++it)
{
TestInfo info;
try
{
bool result = (*it)(info);
if (info.isWantException)
{
info.isOK = false;
}
else
{
info.isOK = result;
}
}
catch (...)
{
info.exception = "有异常";
if (info.isWantException)
{
info.isOK = true;
}
}
testShow(info);
}
}
public:
std::vector<TestFun> m_Tests;
};
TestBaseEX中主要是一个OnTest方法,该方法逻辑比较简单:
循环遍历Command对象并执行,如果期望抛出异常而没有抛异常,则测试部通过,否则根据返回值判断  是否测试通过。
传入的testShow函数对象负责对Command对象测试结果进行处理(一般是进行展示,如果通过显示为绿色,不通过,显示为红色)。 
在创建Command对象的时候,信息越全越好,最好能将函数,参数什么的都包含进行,所以添加了一个宏,来创建Command对象。
    
    
    
/**
* @brief 添加测试对象。
*
*/
#define TEST_INIT(info, sub) { \
ostringstream oss;\
oss<<"position:"<<__FILE__<<"-"<<__LINE__<<"-"<<__FUNCTION__<<endl;\
info.name = __FUNCTION__;/*oss.str();*/\
}\
info.subName = sub;\
info.remark = "";\
info.isOK = true;
#define TESTFUN_INIT(name) m_Tests.push_back(std::bind(&name, this, std::tr1::placeholders::_1))
真正的测试类会继承自 TestBaseEX,例如HiDB的测试类:
     
     
     
/**
* @brief 数据库操作测试类。
*
*/
class HisDBTest: public TestBaseEX
{
public:
HisDBTest();
~HisDBTest();
private:
/**
* @brief 执行Open接口测试(连接字符串正确)。
* @param[in] info 测试数据对象
* @retval true:成功,false;失败
*/
bool OnOpen(TestInfo& inf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值