整理自《C++编程思想》:防御性编程一章
简介
防御性编程:寓意为编写具有健壮性的程序,本节介绍常用的方法
1. 断言
作用:排查项目程序设计错误(发布的程序最好没有断言)
举例:
测试人员使用的测试版本,并不包含断言功能。当测试发现问题后,研发人员可以提供断言版本的程序,复现问题并定位原因
1.1 断言定义
断言格式:
assert(expression);
当表达式expression为true,代码继续运行
当表达式expression为false,打印出调试信息,然后程序退出
调试信息格式为:
sourcefilename:line: function: Assertion `[#cond…str]’ failed.
1.2 设计断言
#include <cstdio>
#include <cstdlib>
#include <iostream>
void __my_assert(char const *srcfile,unsigned int line, char const *func,
char const *assert)
{
using namespace std;
cout << srcfile << ": in line "<< line << " function: "<< func <<" Assertion [ "
<< assert << " ] failed" << endl;
abort();
}
#ifdef NDEBUG
#define assert(cond) ((void)0)
#else
#define assert(cond) ((cond)? (void)0 : __my_assert(__FILE__,__LINE__,__FUNCTION__,#cond))
#endif
int main(void)
{
bool cond;
/*OK*/
assert(cond = true);
/*Nok*/
assert(cond = false);
return 0;
}
输出:
assert.cpp: in line 28 function: main Assertion [ cond = false ] failed
Aborted (core dumped)
1.3 系统工具
标准C库支持一个断言函数
NAME
assert - abort the program if assertion is false
SYNOPSIS
#include <assert.h>
void assert(scalar expression);
DESCRIPTION
If the macro NDEBUG was defined at the moment <assert.h> was last
included, the macro assert() generates no code, and hence does nothing
at all. Otherwise, the macro assert() prints an error message to stan‐
dard error and terminates the program by calling abort(3) if expression
is false (i.e., compares equal to zero).
The purpose of this macro is to help programmers find bugs in their
programs. The message "assertion failed in file foo.c, function
do_bar(), line 1287" is of no help at all to a user.
RETURN VALUE
No value is returned.
ATTRIBUTES
For an explanation of the terms used in this section, see
attributes(7).
┌──────────┬───────────────┬─────────┐
│Interface │ Attribute │ Value │
├──────────┼───────────────┼─────────┤
│assert() │ Thread safety │ MT-Safe │
└──────────┴───────────────┴─────────┘
CONFORMING TO
POSIX.1-2001, POSIX.1-2008, C89, C99. In C89, expression is required
to be of type int and undefined behavior results if it is not, but in
C99 it may have any scalar type.
2. 单元测试
作用:测试代码片段的功能实现,边界测试(发布的程序不包含单元测试代码)
举例:
需求变动添加或者改动函数接口。为了针对性测试特定接口功能,单独编写的测试代码
2.1 设计单元测试
源码目录如下:
编译方法:g++ Date.cpp DateTest.cpp TestSuite/test.cpp
输出结果:
Report for Class: 8DateTest
3[Passed tests] 0[Failed tests]
- 首先设计一个类 Date
写一个与日期有关得类,它将包含以下接口
/*Date.h*/
#include <string>
class Date
{
public:
struct Duration
{
public:
int years;
int months;
int days;
Duration(int ys,int ms,int ds);
};
int m_year;
int m_month;
int m_day;
Date();
Date(int year,int month,int day);
Date(std::string const& date);
virtual ~Date();
int getYear() const;
int getMonth() const;
int getDay() const;
std::string toString() const;
friend bool operator > (Date const& lfs,Date const &rhs);
friend bool operator < (Date const& lfs,Date const &rhs);
friend bool operator >= (Date const& lfs,Date const &rhs);
friend bool operator <= (Date const& lfs,Date const &rhs);
friend bool operator == (Date const& lfs,Date const &rhs);
friend bool operator != (Date const& lfs,Date const &rhs);
friend Duration duration(Date const& lfs, Date const& rhs);
};
/*Date.cpp*/
#include <string>
#include "Date.h"
/*
struct Duration
{
public:
int years;
int months;
int days;
Duration(int ys,int ms,int ds);
};
int m_year;
int m_month;
int m_day;
*/
Date::Date(){}
Date::Date(int year,int month,int day):m_year(year),m_month(month),m_day(day){}
Date::Date(std::string const& date){}
Date::~Date(){}
int Date::getYear() const
{
return m_year;
}
int Date::getMonth() const
{
return m_month;
}
int Date::getDay() const
{
return m_day;
}
std::string Date::toString() const
{
}
bool operator > (Date const& lfs,Date const &rhs);
bool operator < (Date const& lfs,Date const &rhs);
bool operator >= (Date const& lfs,Date const &rhs);
bool operator <= (Date const& lfs,Date const &rhs);
bool operator == (Date const& lfs,Date const &rhs);
bool operator != (Date const& lfs,Date const &rhs);
Date::Duration duration(Date const& lfs, Date const& rhs);
- 先写测试
第一步:写通用测试接口(可以从互联网上下载开源框架)
/*TestSuite/test.h*/
#include <iostream>
#include <typeinfo>
using std::string;
using std::ostream;
using std::cout;
namespace TestSuite
{
#define test_(cond) do_test((cond),#cond,__FILE__,__LINE__)
#define fail_(str) do_fail(#str,__FILE__,__LINE)
class test
{
public:
test(void);
virtual ~test (void);
virtual void run(void) = 0;
void do_test(bool cond, char const *cond_str,char const *file,int line);
void do_fail(char const *info,char const *file, int line);
void succeed_(void);
int getnum_Passed(void);
int getnum_Failed(void);
void report(void);
void reset(void);
ostream *set_Ostream(ostream* new_os);
ostream* get_Ostream(void);
private:
int m_nPassed;
int m_nFailed;
ostream * ostr;
test(test const& that);
test & operator= (test const& rhs);
};
};
/*TestSuite/test.cpp*/
#include <iostream>
#include <typeinfo>
#include "test.h"
namespace TestSuite
{
test::test(void) : m_nPassed(0),m_nFailed(0),ostr(&cout){}
test::~test (void){}
void test::do_test(bool cond, char const *cond_str,char const *file,
int line)
{
cond? succeed_() : do_fail(cond_str,file,line);
}
void test::do_fail(char const *info,char const *file, int line)
{
m_nFailed++;
*ostr << typeid(*this).name() << ":" << std::endl;
*ostr << "Line : " << line << "Under File "
<< file << " Failed [" << info << "]" << std::endl;
}
void test::succeed_(void)
{
m_nPassed++;
}
int test::getnum_Passed(void)
{
return m_nFailed;
}
int test::getnum_Failed(void)
{
return m_nPassed;
}
void test::report(void)
{
*ostr << "Report for Class: " << typeid(*this).name() << std::endl;
*ostr << m_nPassed << "[Passed tests] " << m_nFailed << "[Failed tests]"
<< std::endl;
}
void test::reset(void)
{
m_nPassed=m_nFailed=0;
}
ostream *test::set_Ostream(ostream* new_os)
{
ostream *old = ostr;
ostr=new_os;
return old;
}
ostream* test::get_Ostream(void)
{
return ostr;
}
}
- 写Date测试接口
/*DateTest.cpp*/
#include "Date.h"
#include "TestSuite/test.h"
class DateTest : public TestSuite::test
{
public:
DateTest(void):m_iday(2020,1,6)
{
}
~ DateTest(void){}
void run(void)
{
test_(m_iday.getYear() == 2020);
test_(m_iday.getMonth() == 1);
test_(m_iday.getDay() == 6);
}
private:
Date m_today;
Date m_strday;
Date m_iday;
};
int main()
{
DateTest test;
test.run();
test.report();
return 0;
}
2.2 设计单元测试套件
2.1 一节利用class test设计了class DateTest并进行了测试
通常一个程序设计中应用到多个类,就应当设计多个测试类
这样可以再设计一个类把多个测试类封装在一起集中测试叫做Suite
代码设计略…
2.3 第三方UT工具
参考:
https://blog.youkuaiyun.com/u013176681/article/details/49661667