Poco库中提供了一套小巧的单元测试框架,可以用来对C++代码进行测试。这里介绍一下Poco库中的单元测试框架的使用方法,首先在工程中引入单元测试模块CppUnit,同时包含对应的库的头文件目录。添加完成之后,我们在工程中添加主测试集,测试集是测试程序的入口类,对应的实现如下:
//MainTestSuite.h
#ifndef _MAIN_TEST_SUITE_H_
#define _MAIN_TEST_SUITE_H_
#include "CppUnit\TestSuite.h"
class MainTestSuite
{
public:
//测试集的入口
static CppUnit::Test* suite();
};
#endif
//MainTestSuite.cpp
#include "MainTestSuite.h"
#include "SQLiteTest.h"
CppUnit::Test * MainTestSuite::suite()
{
//在主测试集中可以添加别的测试集
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("MainTestSuite");
pSuite->addTest(SQLiteTest::suite());
return pSuite;
}
测试集是可以嵌套的,我们可以在主测试集中加入子测试集,同时在子测试集中再次加入测试集。这里我们以上一遍文章中的数据库访问类为测试对象说明一下测试集的用法。数据库访问类的实现如下所示。
//SqliteDatabase.h
#ifndef _SQLITE_DATA_BASE_H_
#define _SQLITE_DATA_BASE_H_
#include "Poco\Data\SQLite\SQLite.h"
#include "Poco\Data\Session.h"
#include "Poco\Mutex.h"
#include "Poco\Process.h"
//添加需要的的类
using namespace Poco;
using Poco::FastMutex;
using Poco::ProcessHandle;
using Poco::Data::Session;
using Poco::Data::Keywords::now;
using Poco::Data::Keywords::use;
using Poco::Data::Keywords::bind;
using Poco::Data::Keywords::into;
using Poco::Data::Statement;
//数据结构体
class Person {
public:
std::string mName;//姓名
int mAge; //年龄
std::string mSex; //性别
int mHeight; //身高
std::string mId; //身份ID
};
class SqliteDatabase
{
public:
static SqliteDatabase* Instance();
private:
SqliteDatabase();
public:
~SqliteDatabase();
//增加用户
bool addNewPerson(Person& info);
//删除用户
bool deletePersonByID(const std::string& uuid);
//修改用户信息
bool updatePersonByID(const std::string& uuid, Person& info);
//查找用户信息
bool searchPersonByID(std::string uuid, Person& info);
//判断某个用户信息是否存在
bool isPersonInfoExist(const std::string& uuid);
private:
static SqliteDatabase* mInstance;
static FastMutex mutex;
};
#endif
//SqliteDatabase.cpp
#include "SqliteDatabase.h"
#include "Poco\Data\SQLite\Connector.h"
SqliteDatabase* SqliteDatabase::mInstance = nullptr;
FastMutex SqliteDatabase::mutex;
//单例模式,doublecheck,线程安全
SqliteDatabase * SqliteDatabase::Instance()
{
if (mInstance == nullptr)
{
mutex.lock();
if (mInstance == nullptr)
{
mInstance = new SqliteDatabase();
}
mutex.unlock();
}
return mInstance;
}
SqliteDatabase::SqliteDatabase()
{
Poco::Data::SQLite::Connector::registerConnector();
}
SqliteDatabase::~SqliteDatabase()
{
Poco::Data::SQLite::Connector::unregisterConnector();
}
bool SqliteDatabase::addNewPerson(Person & info)
{
Session session(Poco::Data::SQLite::Connector::KEY, "test.db");
session << "CREATE TABLE IF NOT EXISTS PERSONINFO (UUID VARCHAR(64), NAME VARCHAR(64), AGE INTEGER(3), SEX VARCHAR(64), HEIGHT INTEGER(3))", now;
if (isPersonInfoExist(info.mId))
{
return false;
}
//采用绑定的形式防止SQL注入
Statement stmt = (session << "INSERT INTO PERSONINFO VALUES(:uuid, :name, :age, :sex, :height)",
bind(info.mId), bind(info.mName), bind(info.mAge), bind(info.mSex), bind(info.mHeight));
return (stmt.execute() > 0);
}
bool SqliteDatabase::deletePersonByID(const std::string & uuid)
{
Session session(Poco::Data::SQLite::Connector::KEY, "test.db");
Statement stmt = (session << "DELETE FROM PERSONINFO WHERE UUID = :uuid", bind(uuid.c_str()));
return (stmt.execute() == 1);
}
bool SqliteDatabase::updatePersonByID(const std::string & uuid, Person & info)
{
Session session(Poco::Data::SQLite::Connector::KEY, "test.db");
Statement stmt = (session << "UPDATE PERSONINFO SET NAME = :name, AGE = :age, SEX = :sex, HEIGHT = :height WHERE UUID = :uuid",
bind(info.mName), bind(info.mAge), bind(info.mSex), bind(info.mHeight), bind(uuid));
return (stmt.execute() == 1);
}
bool SqliteDatabase::searchPersonByID(std::string uuid, Person & info)
{
Session session(Poco::Data::SQLite::Connector::KEY, "test.db");
int age = 0;
int height = 0;
std::string name;
std::string sex;
Statement stmt = (session << "SELECT NAME, AGE, SEX, HEIGHT FROM PERSONINFO WHERE UUID = :uuid",
into(name), into(age), into(sex), into(height), bind(uuid));
if (stmt.execute() == 1)
{
info.mAge = age;
info.mName = name;
info.mSex = sex;
info.mHeight = height;
info.mId = uuid;
return true;
}
return false;
}
bool SqliteDatabase::isPersonInfoExist(const std::string & uuid)
{
Session session(Poco::Data::SQLite::Connector::KEY, "test.db");
Statement stmt = (session << "SELECT * from PERSONINFO WHERE UUID = :uuid", bind(uuid));
if (stmt.execute() == 1)
{
return true;
}
return false;
}
对应的测试集实现如下:
//SQLiteTest.h
#ifndef _SQLTE_TEST_H_
#define _SQLTE_TEST_H_
#include "CppUnit/TestCase.h"
class SQLiteTest : public CppUnit::TestCase
{
public:
SQLiteTest(const std::string & name);
~SQLiteTest();
//测试数据插入
void testInsertSingleItem();
//测试数据删除
void testDeleteSingleItem();
//测试数据更新
void testUpdateSingleItem();
//测试数据查找
void testSearchSingleItem();
public:
static CppUnit::Test* suite();
};
#endif
//SQLiteTest.cpp
#include "SQLiteTest.h"
#include "CppUnit/CppUnit.h"
#include "CppUnit/TestSuite.h"
#include "CppUnit/TestCaller.h"
#include "SqliteDatabase.h"
#include <string>
SQLiteTest::SQLiteTest(const std::string & name) :CppUnit::TestCase(name)
{
}
SQLiteTest::~SQLiteTest()
{
}
void SQLiteTest::testInsertSingleItem()
{
Person insert_person;
insert_person.mAge = 12;
insert_person.mHeight = 168;
insert_person.mName = "Liming";
insert_person.mId = "6335626";
insert_person.mSex = "female";
SqliteDatabase* base = SqliteDatabase::Instance();
assertTrue(base->addNewPerson(insert_person));
assertTrue(base->isPersonInfoExist("6335626"));
assertTrue(!base->addNewPerson(insert_person));
}
void SQLiteTest::testDeleteSingleItem()
{
Person insert_person;
insert_person.mAge = 12;
insert_person.mHeight = 168;
insert_person.mName = "Liming";
insert_person.mId = "6335620";
insert_person.mSex = "female";
SqliteDatabase* base = SqliteDatabase::Instance();
assertTrue(base->addNewPerson(insert_person));
assertTrue(base->deletePersonByID("6335620"));
assertTrue(!base->isPersonInfoExist("6335620"));
}
void SQLiteTest::testUpdateSingleItem()
{
Person insert_person;
insert_person.mAge = 12;
insert_person.mHeight = 168;
insert_person.mName = "Liming";
insert_person.mId = "6335920778";
insert_person.mSex = "female";
SqliteDatabase* base = SqliteDatabase::Instance();
assertTrue(base->addNewPerson(insert_person));
Person new_person;
new_person.mAge = 19;
new_person.mHeight = 188;
new_person.mName = "Liming";
new_person.mSex = "female";
assertTrue(base->updatePersonByID("6335920778", new_person));
Person after_update;
assertTrue(base->searchPersonByID("6335920778", after_update));
assertTrue(after_update.mAge == 19);
assertTrue(after_update.mHeight == 188);
}
void SQLiteTest::testSearchSingleItem()
{
Person insert_person;
insert_person.mAge = 12;
insert_person.mHeight = 168;
insert_person.mName = "Liming";
insert_person.mId = "6339920";
insert_person.mSex = "female";
SqliteDatabase* base = SqliteDatabase::Instance();
assertTrue(base->addNewPerson(insert_person));
Person newInfo;
assertTrue(base->searchPersonByID("6339920", newInfo));
assertTrue(newInfo.mId == "6339920");
assertTrue(newInfo.mHeight == 168);
assertTrue(newInfo.mName == "Liming");
assertTrue(insert_person.mSex == "female");
}
//将测试用例添加到测试集中
CppUnit::Test * SQLiteTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SQLiteTest");
CppUnit_addTest(pSuite, SQLiteTest, testInsertSingleItem);
CppUnit_addTest(pSuite, SQLiteTest, testDeleteSingleItem);
CppUnit_addTest(pSuite, SQLiteTest, testSearchSingleItem);
CppUnit_addTest(pSuite, SQLiteTest, testUpdateSingleItem);
//添加耗时比较长的测试
//CppUnit_addLongTest(pSuite, FileChannelTest, testRotateAtTimeHourUTC);
return pSuite;
}
测试集添加完成之后我们就可以定义程序的入口了,这里我们采用单元测试的宏来定义程序的入口。对应的实现如下:
//Driver.cpp
#include "CppUnit/TestRunner.h"
#include "MainTestSuite.h"
#include "CppUnit\TestRunner.h"
//定义了主测试集
CppUnitMain(MainTestSuite)
这时候运行测试程序会发现程序闪退。这是因为我们没有指定测试的输出模式,Poco的测试框架支持多种输出形式,都是通过命令行参数来进行配置的。常用的参数如下所示:
-all 执行所有的测试
-wait 执行完毕之后进行等待
-ignore *** 忽略哪些测试
-setup *** 执行哪些测试
-print 打印测试用例的详细信息
-long 执行耗时比较长的测试用例
测试输出结果如下图所示: