防御型编程

整理自《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]

  1. 首先设计一个类 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);

  1. 先写测试
    第一步:写通用测试接口(可以从互联网上下载开源框架)
/*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;
		}
}
  1. 写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.csdn.net/u013176681/article/details/49661667

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值