个人编程中比较喜欢重构,重构能够提高自己的代码质量,使代码阅读起来也更清晰。
但是重构有一个问题,就是如何保证重构后带代码实现的功能与重构前的一致;如果每次重构完成后,对此不闻不问,则会有极大的风险,如果每次重构后,都进行一边测试,则工作量会很巨大,最终可能是即使代码有重构的欲望,也会尽量克制住,不去重构。除非代码能够进行自动化测试。
实际上进行测试的是接口,而不是所有代码,只要能够保持接口不变,自动化测试的工作量也没有想象中的巨大。其实我们在单元测试的时候,会测试各种异常情况,只不过,没有将这些测试写成测试代码罢了。
typedef std::function<bool(TestInfo&)> TestFun;
/*** @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; /**< 备注信息 */};
/*** @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;};
/*** @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))
/*** @brief 数据库操作测试类。**/class HisDBTest: public TestBaseEX{public:HisDBTest();~HisDBTest();private:/*** @brief 执行Open接口测试(连接字符串正确)。* @param[in] info 测试数据对象* @retval true:成功,false;失败*/bool OnOpen(TestInfo& info);bool DropTable(TestInfo&);bool CreateTable(TestInfo&);bool AddRecorder(TestInfo&);bool AddRecorder2(TestInfo&);bool Scalar(TestInfo&);bool Scalar2(TestInfo&);bool Scalar3(TestInfo&);bool ReadRecorders(TestInfo&);bool ReadRecorders2(TestInfo&);bool ReadRecorders3(TestInfo&);bool DeleteRecorder(TestInfo&);bool OnClose(TestInfo&);bool OnClose_Repeat(TestInfo&);bool OnConnRelace2(TestInfo&);bool OnConnRelace3(TestInfo&);private:HiDB* m_DB;};
using namespace std;HisDBTest::HisDBTest(){TESTFUN_INIT(HisDBTest::OnOpen);TESTFUN_INIT(HisDBTest::DropTable);TESTFUN_INIT(HisDBTest::CreateTable);TESTFUN_INIT(HisDBTest::AddRecorder);TESTFUN_INIT(HisDBTest::AddRecorder2);TESTFUN_INIT(HisDBTest::Scalar);TESTFUN_INIT(HisDBTest::Scalar2);TESTFUN_INIT(HisDBTest::Scalar3);TESTFUN_INIT(HisDBTest::ReadRecorders);TESTFUN_INIT(HisDBTest::ReadRecorders2);TESTFUN_INIT(HisDBTest::ReadRecorders3);TESTFUN_INIT(HisDBTest::DeleteRecorder);TESTFUN_INIT(HisDBTest::OnConnRelace2);TESTFUN_INIT(HisDBTest::OnConnRelace3);this->m_DB = new HiDB(HiDBType_MySQL, false);}HisDBTest::~HisDBTest(){if (this->m_DB){this->m_DB->Close();delete this->m_DB;this->m_DB = NULL;}}bool HisDBTest::OnOpen(TestInfo& info){TEST_INIT(info, "打开数据库");info.remark = "(请提供数据库:host=127.0.0.1;port=3306;""dbname=test;user=root;pwd=root;charset=gbk;";return this->m_DB->Open("host=127.0.0.1;port=3306;dbname=test;user=root;pwd=root;charset=gbk;");}bool HisDBTest::DropTable(TestInfo& info){TEST_INIT(info, "ExecuteNoQuery 无参");return this->m_DB->ExecuteNoQuery("drop table if exists table1;");}bool HisDBTest::CreateTable(TestInfo& info){TEST_INIT(info, "ExecuteNoQuery 无参");return this->m_DB->ExecuteNoQuery("create table table1(column1 varchar(6) not null,""column2 varchar(40) not null,""column3 int not null default 1,""column4 int, ""column5 timestamp not null default CURRENT_TIMESTAMP,""column6 varchar(512),primary key (column1));");}bool HisDBTest::AddRecorder(TestInfo& info){TEST_INIT(info, "ExecuteNoQuery C语言方式(printf)");return this->m_DB->ExecuteNoQuery("INSERT INTO table1(Column1,Column2,Column3,Column4,Column6) ""VALUES(‘%s‘, ‘%s‘, %d, NULL, ‘%s‘)","mytest", "my second test recorder",80, "this test create by xuminrong");}bool HisDBTest::AddRecorder2(TestInfo& info){TEST_INIT(info, "Create方法,自动组成SQL语句");vector<HiDBParamer> paramers;HiDBParamer paramer1("column1", "hitest");paramers.push_back(paramer1);HiDBParamer paramer2("column2", "this is a test by xmr");paramers.push_back(paramer2);HiDBParamer paramer3(HiDBDataType_Short, "column3", "59");paramers.push_back(paramer3);HiDBParamer paramer4(HiDBDataType_Short, "column4", "59");paramer4.m_IsNull = true;paramers.push_back(paramer4);HiDBParamer paramer6("column6", "this is a test");// = {0};paramers.push_back(paramer6);return this->m_DB->Create("table1", paramers);}bool HisDBTest::Scalar(TestInfo& info){TEST_INIT(info, "ExecuteScalar 使用参数数组,以HiDBRetVal作为返回值");vector<HiDBParamer> paramers;HiDBParamer paramer1("column1", "hitest");paramers.push_back(paramer1);HiDBParamer paramer2(HiDBDataType_Short, "column3", "59");paramers.push_back(paramer2);HiDBRetVal val;try{if (!this->m_DB->ExecuteScalar("SELECT column6 FROM table1 WHERE ? AND ?",paramers, &val)){return false;}}catch (HiDBException& e){info.remark = "产生HiDBException异常:" + e.m_descript + e.m_position;}if (::strcmp(val.m_Value.c_str(), "this is a test") != 0){return false;}return true;}bool HisDBTest::Scalar2(TestInfo& info){TEST_INIT(info, "ExecuteScalar C语言方式(printf),以string作为返回值");string val;try{return this->m_DB->ExecuteScalar("SELECT column4 FROM table1 WHERE column1=‘%s‘ AND column3=%d",&val, "hitest", 59);}catch (HiDBException& e){info.remark = "产生HiDBException异常:" + e.m_descript + e.m_position;return false;}}bool HisDBTest::Scalar3(TestInfo& info){TEST_INIT(info, "ExecuteScalar C语言方式(printf),返回空值");HiDBRetVal val;try{if (!this->m_DB->ExecuteScalar("SELECT column4 FROM table1 WHERE column1=‘%s‘ AND column3=%d",&val, "hitest", 59)){return false;}}catch (HiDBException& e){info.remark = "产生HiDBException异常:" + e.m_descript + e.m_position;return false;}if (val.m_IsNull){return true;}else{return false;}}bool HisDBTest::ReadRecorders(TestInfo& info){TEST_INIT(info, "ExecuteQuery 使用参数数组");vector<HiDBParamer> paramers;HiDBParamer paramer1("column1", "hitest");paramers.push_back(paramer1);HiDBParamer paramer2( "column1", "mytest");paramers.push_back(paramer2);std::shared_ptr<HiDBTable> table = this->m_DB->ExecuteQuery("SELECT column1,column2 FROM table1 WHERE ? OR ? ORDER BY column1", paramers);if (table == NULL){return false;}if (table->size() != 2){return false;}ostringstream oss;for (auto it = table->begin(); it != table->end(); ++it){for(auto item = (*it).begin(); item != (*it).end(); ++item){//oss<<"field:"<<item->second.m_Field.c_str()<<",value:"<<item->second.m_Value.c_str()<<"\r\n";oss<<item->second.ToSrting()<<"\n";}}info.remark.append(oss.str().c_str());return true;}bool HisDBTest::ReadRecorders2(TestInfo& info){TEST_INIT(info, "ExecuteQuery C语言方式(printf)");std::shared_ptr<HiDBTable> table = this->m_DB->ExecuteQuery("SELECT column1,column2 FROM table1 WHERE column1=‘%s‘ OR column1=‘%s‘","hitest", "mytest");if (table == NULL){return false;}if (table->size() != 2){return true;}ostringstream oss;for (auto it = table->begin(); it != table->end(); ++it){for(auto item = (*it).begin(); item != (*it).end(); ++item){//oss<<"field:"<<item->second.m_Field.c_str()<<",value:"<<item->second.m_Value.c_str()<<"\r\n";oss<<item->second.ToSrting()<<"\n";}}info.remark.append(oss.str().c_str());return true;}bool HisDBTest::ReadRecorders3(TestInfo& info){TEST_INIT(info, "生成SQL语句测试");vector<HiDBParamer> list;HiDBParamer parm1("nCameraNo", 12345);list.push_back(parm1);HiDBParamer parm2(HiDBDataType_Time, "dtTime", "2012-08-06 16:44:32");parm2.m_Oper = HiOper_GreaterEqual;list.push_back(parm2);HiDBParamer parm3(HiDBDataType_Time, "dtTime", "2012-08-07 16:44:32");parm3.m_Oper = HiOper_LessEqual;list.push_back(parm3);HiDBParamer parm4("nEPType", 3);list.push_back(parm4);HiDBParamer parm6( "nFoward", 5);list.push_back(parm6);ostringstream oss;oss<<"SELECT nCameraNo,nCarNoType,cCarNo,dtTime,cAddress,""cRouteChannel,nFoward,nEPType,nCaptureType,cAction,""nTotalTime,nColor FROM Illegals";int count = (int)list.size();if (count > 0){oss<< " WHERE ";}for (int i = 0; i < count; i++){oss<<" ? ";if (i != count - 1){oss<<" AND ";}}oss<<" ORDER BY nNo LIMIT "<<3<<" "<<50<<endl;try{string sql = oss.str();std::shared_ptr<HiDBTable> table = this->m_DB->ExecuteQuery(sql.c_str(),list);if (table == NULL){return true;}return false;}catch (HiDBException& ex){info.remark = ex.m_sql;return true;}catch (...){return false;}}bool HisDBTest::DeleteRecorder(TestInfo& info){TEST_INIT(info, "Delete 使用参数数组");vector<HiDBParamer> paramers;HiDBParamer paramer1("column1", "hitest");paramers.push_back(paramer1);HiDBParamer paramer2( HiDBDataType_Short, "column3", "59");paramers.push_back(paramer2);return this->m_DB->Delete("table1", paramers);}static string getConn(string ip, TestInfo& info);bool HisDBTest::OnConnRelace2(TestInfo& info){TEST_INIT(info, "替换数据库IP");return getConn("127.0.15.43", info)=="host=127.0.0.1;port=3306;""dbname=test;user=root;pwd=root;charset=gbk;";}bool HisDBTest::OnConnRelace3(TestInfo& info){TEST_INIT(info, "替换数据库IP");return getConn("127.1.5.1", info)=="host=127.1.5.1;port=3306;""dbname=test;user=root;pwd=root;charset=gbk;";}static string getConn(string m_CenterIP, TestInfo& info){string conn = "host=127.0.0.1;port=3306;""dbname=test;user=root;pwd=root;charset=gbk;";if (!m_CenterIP.empty()){string::size_type pos1 = conn.find_first_of(‘=‘);string::size_type pos2 = conn.find_first_of(‘;‘, pos1);//取数据库IPstring ip = conn.substr(pos1 + 1, pos2 - pos1 - 1);string::size_type pos_connect = ip.find_first_of(‘.‘, ip.find_first_of(‘.‘) + 1);string::size_type pos_center = m_CenterIP.find_first_of(‘.‘,m_CenterIP.find_first_of(‘.‘) + 1);//比较IP前两段是否一样if (ip.substr(0, pos_connect) != m_CenterIP.substr(0, pos_center)){conn.replace(pos1 + 1, ip.size(), m_CenterIP);}}return conn;}
原文地址:http://blog.csdn.net/xumingxsh/article/details/38708365