您的位置:首页 > 其它

一个精简的测试框架及使用示例

2012-09-03 11:46 495 查看
一个简单的测试框架,代码简洁,不需要安装。

下面是其源码以及使用示例:

1、testframe文件夹

mock.h

#include <Windows.h>
#include <map>

#pragma warning (disable : 4311)
struct ByteSave
{
unsigned char code[5];
};

class CFuncMock
{
typedef std::map<void*, ByteSave> map_addr;
map_addr m_mapAddr;

CFuncMock(){ }

public:
~CFuncMock()
{
CancelAll();
}

bool SetHook(void* pOldFunc, void* pMockFunc)
{
DWORD dwOldProtect;

if ( !VirtualProtect(pOldFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
return false;
}

if ( m_mapAddr.find(pOldFunc) == m_mapAddr.end() )
{
ByteSave cp_code;

for (int i=0; i<5; i++)
{
cp_code.code[i] = ((char*)pOldFunc)[i];
}

m_mapAddr[pOldFunc] = cp_code;
}

BYTE hook_code[5] = {0xe9, 0, 0, 0};

*(unsigned int*)(hook_code + 1) = (unsigned int)pMockFunc - (unsigned int)pOldFunc - 5;

for (int i=0; i<5; i++)
{
((char*)pOldFunc)[i] = hook_code[i];
}

return true;
}

bool CancelHook(void* pOldFunc)
{
map_addr::iterator itm = m_mapAddr.find(pOldFunc);

if ( itm == m_mapAddr.end() )
{
return false;
}

for (int i=0; i<5; i++)
{
((char*)itm->first)[i] = itm->second.code[i];
}

m_mapAddr.erase(itm);

return true;
}

void CancelAll()
{
for (map_addr::iterator itm=m_mapAddr.begin();
itm!=m_mapAddr.end(); ++itm)
{
for (int i=0; i<5; i++)
{
((char*)itm->first)[i] = itm->second.code[i];
}
}

m_mapAddr.clear();
}

static CFuncMock* GetInstance()
{
static CFuncMock s_Instance;

return &s_Instance;
}
};

#define HOOK_SET(_old, _new)    CFuncMock::GetInstance()->SetHook(_old, _new)
#define HOOK_RESET(_old)    CFuncMock::GetInstance()->CancelHook(_old)
#define HOOK_CLEAR()    CFuncMock::GetInstance()->CancelAll()


TestCase.h

//TestCase.h
#pragma once

#include <iostream>

class TestCase;

typedef TestCase* (*LPCreateCaseFun)();
typedef void (TestCase::*LPUnitFun)();

typedef struct tagUnitData
{
const char* strName;
LPUnitFun pFun;

tagUnitData* pNext;
}UnitData;

typedef struct tagCaseData
{
const char* strName;
LPCreateCaseFun pFun;
UnitData* pUnitData;

tagCaseData* pNext;
}CaseData;

enum UnitResult{UNIT_RESULT_PASS, UNIT_RESULT_FAIL, UNIT_RESULT_ERROR};

class TestCase
{
public:
static void AddCase(const char* strName, LPCreateCaseFun pFun, UnitData* pUnitData);
static void AddUnit(UnitData** ppUnitData, UnitData** ppHead, const char* lpUnitName, LPUnitFun lpUnitFun);
static void RunAllUnit();
static void RunClassUnit(const char* lpClassName);
static void RunSingleUnit(const char* lpClassName, const char* lpUnitName);

public:
TestCase(){};
virtual ~TestCase(){};
virtual void SetUp(){};
virtual void TearDown(){};

private:
class _FreeMemory_
{
public:
~_FreeMemory_();
};

private:
class _TimeCount_
{
public:
_TimeCount_();
~_TimeCount_();
private:
unsigned long dwBegin;
};

private:
static CaseData* s_pCaseData;
static CaseData* s_pCaseDataHead;
static _FreeMemory_ _temp_;
static CaseData* FindCase(const char* lpCaseName);
static UnitData* FindUnit(CaseData* pCaseData, const char* lpUnitName);
static UnitResult RunUnit(TestCase* pTestCase, UnitData* pUnitData);
static void PrintTab(const char* strName);
static void PrintResult(int nPassCount, int nFailCount, int nErrorCount);
static void FreeMemory();
};

#define DECLARE_CASE_BEGIN(CLASS) private:static char _c_;\
static TestCase* _CreateInstance_(){return new CLASS();}\
static char _AddCase_(){UnitData* pUnitData = NULL;UnitData* pHead = NULL;const char* lpCaseName = #CLASS;
#define DECLARE_UNIT(CLASS, UNIT) AddUnit(&pUnitData, &pHead, #UNIT, static_cast<LPUnitFun>(&CLASS::UNIT));
#define DECLARE_CASE_END() TestCase::AddCase(lpCaseName, &_CreateInstance_, pHead);return 0;}
#define IMPLEMENT_CASE(CLASS) char CLASS::_c_ = _AddCase_();

#define PRINT_FAIL_MSG std::cout << "Failure. "
#define PRINT_FILE_AND_LINE std::cout << "File:" << __FILE__ << ", Line:" << __LINE__ << std::endl
#define THROW_ERROR throw 0
#define PRINT_NO_MSG PRINT_FAIL_MSG; PRINT_FILE_AND_LINE; THROW_ERROR
#define AssertTrue(condition) if (!(condition)) {PRINT_NO_MSG;}
#define AssertFalse(condition) if (condition) {PRINT_NO_MSG;}
#define AssertNull(pointer) if (NULL != (pointer)) {PRINT_NO_MSG;}
#define AssertNotNull(pointer) if (NULL == (pointer)) {PRINT_NO_MSG;}
#define AssertEqual(_expected, _actual) if ((_expected) != (_actual)) {PRINT_FAIL_MSG; std::cout << "expected is " << (_expected) << ", but actual is " << (_actual) << ". "; PRINT_FILE_AND_LINE; THROW_ERROR;}
#define AssertStrEqual(_expected, _actual) {std::string stdexpected = (_expected); std::string stdactual = (_actual);  if ((stdexpected) != (stdactual)) {PRINT_FAIL_MSG; std::cout << "expected is " << (stdexpected) << ", but actual is " << (stdactual) << ". "; PRINT_FILE_AND_LINE; THROW_ERROR;}}
#define AssertStrEqual_W(_expected, _actual) {std::wstring stdexpected = (_expected); std::wstring stdactual = (_actual);  if ((stdexpected) != (stdactual)) {PRINT_FAIL_MSG; std::wcout << "expected is " << (stdexpected) << ", but actual is " << (stdactual) << ". "; PRINT_FILE_AND_LINE; THROW_ERROR;}}
#define FailNoReason() PRINT_NO_MSG
#define FailWithReason(reason) PRINT_FAIL_MSG; PRINT_FILE_AND_LINE; std::cout << "\tReason:" << (reason) << std::endl; THROW_ERROR
#define FailWithReason_W(reason) PRINT_FAIL_MSG; PRINT_FILE_AND_LINE; std::wcout << L"\tReason:" << (reason) << std::endl; THROW_ERROR


TestCase.cpp

//TestCase.cpp
#include "TestCase.h"
#include <Windows.h>
// #include "utils/HThread.h"

#define CASE_SPLIT "-----------------------"
#define CANNOT_FIND_CLASS "Cannot find class"
#define CANNOT_FIND_UNIT "Cannot find unit"

TestCase::_FreeMemory_ TestCase::_temp_;
CaseData* TestCase::s_pCaseData = NULL;
CaseData* TestCase::s_pCaseDataHead = NULL;

void TestCase::AddCase(const char* strName, LPCreateCaseFun pFun, UnitData* pUnitData)
{
if (NULL == s_pCaseData)
{
s_pCaseData = new CaseData();
s_pCaseDataHead = s_pCaseData;
}
else
{
s_pCaseData->pNext = new CaseData();
s_pCaseData = s_pCaseData->pNext;
}

s_pCaseData->strName = strName;
s_pCaseData->pFun = pFun;
s_pCaseData->pUnitData = pUnitData;
s_pCaseData->pNext = NULL;
}

void TestCase::AddUnit(UnitData** ppUnitData, UnitData** ppHead, const char* lpUnitName, LPUnitFun lpUnitFun)
{
if (NULL == *ppUnitData)
{
*ppUnitData = new UnitData();
*ppHead = *ppUnitData;
}
else
{
(*ppUnitData)->pNext = new UnitData();
*ppUnitData = (*ppUnitData)->pNext;
}

(*ppUnitData)->strName = lpUnitName;
(*ppUnitData)->pFun = lpUnitFun;
(*ppUnitData)->pNext = NULL;
}

void TestCase::RunAllUnit()
{
_TimeCount_ timecount;
int nPassCount = 0;
int nFailCount = 0;
int nErrorCount = 0;
CaseData* pCaseFind = s_pCaseDataHead;
while (pCaseFind != NULL)
{
std::cout << CASE_SPLIT << pCaseFind->strName << CASE_SPLIT << std::endl;

TestCase* pTestCase = pCaseFind->pFun();
UnitData* pUnitFind = pCaseFind->pUnitData;
while (pUnitFind != NULL)
{
std::cout << pUnitFind->strName;
PrintTab(pUnitFind->strName);

UnitResult ret = RunUnit(pTestCase, pUnitFind);
if (UNIT_RESULT_PASS == ret)
{
nPassCount++;
}
else if (UNIT_RESULT_FAIL == ret)
{
nFailCount++;
}
else
{
nErrorCount++;
}

pUnitFind = pUnitFind->pNext;
}

delete pTestCase;
pTestCase = NULL;

std::cout << std::endl;
pCaseFind = pCaseFind->pNext;
}

PrintResult(nPassCount, nFailCount, nErrorCount);
}

void TestCase::RunClassUnit(const char* lpClassName)
{
_TimeCount_ timecount;
std::cout << CASE_SPLIT << lpClassName << CASE_SPLIT << std::endl;

CaseData* pCaseFind = FindCase(lpClassName);
if (NULL == pCaseFind)
{
std::cout << CANNOT_FIND_CLASS << " '" << lpClassName << "'" << std::endl;
return;
}

int nPassCount = 0;
int nFailCount = 0;
int nErrorCount = 0;
TestCase* pTestCase = pCaseFind->pFun();
UnitData* pUnitFind = pCaseFind->pUnitData;
while (pUnitFind != NULL)
{
std::cout << pUnitFind->strName;
PrintTab(pUnitFind->strName);

UnitResult ret = RunUnit(pTestCase, pUnitFind);
if (UNIT_RESULT_PASS == ret)
{
nPassCount++;
}
else if (UNIT_RESULT_FAIL == ret)
{
nFailCount++;
}
else
{
nErrorCount++;
}

pUnitFind = pUnitFind->pNext;
}

delete pTestCase;
pTestCase = NULL;

PrintResult(nPassCount, nFailCount, nErrorCount);
}

void TestCase::RunSingleUnit(const char* lpClassName, const char* lpUnitName)
{
_TimeCount_ timecount;
std::cout << CASE_SPLIT << lpClassName << CASE_SPLIT << std::endl;

CaseData* pCaseFind = FindCase(lpClassName);
if (NULL == pCaseFind)
{
std::cout << CANNOT_FIND_CLASS << " '" << lpClassName << "'" << std::endl;
return;
}

UnitData* pUnitFind = FindUnit(pCaseFind, lpUnitName);
if (NULL == pUnitFind)
{
std::cout << CANNOT_FIND_UNIT << " '" << lpUnitName << "'" << std::endl;
return;
}

TestCase* pTestCase = pCaseFind->pFun();
std::cout << pUnitFind->strName;
PrintTab(pUnitFind->strName);
RunUnit(pTestCase, pUnitFind);
delete pTestCase;
pTestCase = NULL;
}

CaseData* TestCase::FindCase(const char* lpCaseName)
{
CaseData* pCaseFind = s_pCaseDataHead;
CaseData* pCaseTemp = NULL;
while (pCaseFind != NULL)
{
if (0 == strcmp(lpCaseName, pCaseFind->strName))
{
pCaseTemp = pCaseFind;
break;
}

pCaseFind = pCaseFind->pNext;
}
return pCaseTemp;
}

UnitData* TestCase::FindUnit(CaseData* pCaseData, const char* lpUnitName)
{
UnitData* pUnitFind = pCaseData->pUnitData;
UnitData* pUnitTemp = NULL;
while (pUnitFind != NULL)
{
if (0 == strcmp(lpUnitName, pUnitFind->strName))
{
pUnitTemp = pUnitFind;
break;
}

pUnitFind = pUnitFind->pNext;
}

return pUnitTemp;
}

UnitResult TestCase::RunUnit(TestCase* pTestCase, UnitData* pUnitData)
{

UnitResult ret = UNIT_RESULT_PASS;
pTestCase->SetUp();
try
{
LPUnitFun pFun = pUnitData->pFun;
(pTestCase->*pFun)();
std::cout << "Pass" << std::endl;
}
catch (int)
{
ret = UNIT_RESULT_FAIL;
}
catch (...)
{
ret = UNIT_RESULT_ERROR;
std::cout << "Error" << std::endl;
}
pTestCase->TearDown();

return ret;
}

void TestCase::PrintTab(const char* strName)
{
const int nMaxLength = 30;
size_t nLength = strlen(strName);
std::string strSpace = " ";
for (size_t i = nLength; i < nMaxLength; i++)
{
strSpace += ' ';
}
std::cout << strSpace.c_str();
}

void TestCase::PrintResult(int nPassCount, int nFailCount, int nErrorCount)
{
std::cout << std::endl << "Runs:" << nPassCount + nFailCount + nErrorCount
<< " Failures:" << nFailCount
<< " Errors:" << nErrorCount
<< std::endl;
}

TestCase::_FreeMemory_::~_FreeMemory_()
{
TestCase::FreeMemory();
}

void TestCase::FreeMemory()
{
CaseData* pCaseFind = s_pCaseDataHead;
CaseData* pCaseFind_Temp;
while (pCaseFind != NULL)
{
pCaseFind_Temp = pCaseFind->pNext;

UnitData* pUnitFind = pCaseFind->pUnitData;
UnitData* pUnitFind_Temp;
while (pUnitFind != NULL)
{
pUnitFind_Temp = pUnitFind->pNext;

delete pUnitFind;
pUnitFind = pUnitFind_Temp;
}

delete pCaseFind;
pCaseFind = pCaseFind_Temp;
}
}

TestCase::_TimeCount_::_TimeCount_()
{
dwBegin = GetTickCount();
}

TestCase::_TimeCount_::~_TimeCount_()
{
unsigned long dwEnd = GetTickCount();
double dwCost = static_cast<double>(dwEnd - dwBegin) * 0.001f;
std::cout << "Finished after " << dwCost << "s" << std::endl;
}


TestParam.h

//TestParam.h
#pragma once

#include <string>

class TestParam
{
private:
static TestParam* ins;
static TestParam _tmp_;
static bool bDelete;

private:
TestParam();
~TestParam();

private:
std::string m_strCasePath;

public:
static TestParam* GetInstance();

void Init(const char* lpExePath);
std::string GetCasePath();
};


TestParam.cpp

//TestParam.cpp
#include "TestParam.h"
#include <stdlib.h>

TestParam* TestParam::ins = NULL;
TestParam TestParam::_tmp_;
bool TestParam::bDelete = true;

TestParam::TestParam()
{

}

TestParam::~TestParam()
{
if (bDelete)
{
if (NULL != ins)
{
bDelete = false;
delete ins;
ins = NULL;
}
}
}

TestParam* TestParam::GetInstance()
{
if (NULL == ins)
{
ins = new TestParam();
}
return ins;
}

void TestParam::Init(const char* lpExePath)
{
std::string strExePath = lpExePath;
for (size_t i = 0; i < strExePath.length(); i++)
{
if (strExePath[i] == '\\')
{
strExePath[i] = '/';
}
}

size_t nPos = strExePath.rfind('/');
nPos = strExePath.rfind('/', nPos - 1);
m_strCasePath = strExePath.substr(0, nPos + 1) + "TestCase/";
}

std::string TestParam::GetCasePath()
{
return m_strCasePath;
}


2、src文件夹

Demo.h

//Demo.h
#pragma once

//// 辅助函数,用来被mock
//int fun();

class CDemo
{
public:
CDemo(void);
~CDemo(void);

void Func1(void);
void Func2(void);
};


Demo.cpp

//Demo.cpp
#include "Demo.h"
#include <iostream>
using namespace std;

//// 辅助函数,用来被mock
//int fun()
//{
//    return 10;
//}

CDemo::CDemo(void)
{
}

CDemo::~CDemo(void)
{
}

void CDemo::Func1()
{
cout << "***Func1***" << endl;
//     cout << fun() << endl;
}

void CDemo::Func2()
{
cout << "***Func2***" << endl;
}


3、case文件夹

TestDemo.h

//TestDemo.h
#pragma once
#include "TestCase.h"
#include "mock.h"

class TestDemo : public TestCase
{
public:
DECLARE_CASE_BEGIN(TestDemo)
DECLARE_UNIT(TestDemo, TestFunc1)
DECLARE_UNIT(TestDemo, TestFunc2)
DECLARE_UNIT(TestDemo, TestMockFunc)
DECLARE_CASE_END()

public:
TestDemo(void);
~TestDemo(void);

void TestDemo::SetUp();
void TestDemo::TearDown();

void TestFunc1();
void TestFunc2();

void TestMockFunc();
};


TestDemo.cpp

//TestDemo.cpp
#include <Windows.h>
#include "TestDemo.h"
#include "Demo.h"

#include <iostream>
using namespace std;

int Mock()
{
return 1000;
}

int fun()
{
return 10;
}

IMPLEMENT_CASE(TestDemo)

void TestDemo::SetUp()
{
// 用Mock函数来替换fun函数
// mock对于某些不容易构造或者 不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
HOOK_SET(&fun, &Mock);
}

void TestDemo::TearDown()
{
HOOK_CLEAR();
}

TestDemo::TestDemo(void)
{
}

TestDemo::~TestDemo(void)
{
}

void TestDemo::TestFunc1()
{
CDemo demo;
demo.Func1();
}

void TestDemo::TestFunc2()
{
CDemo demo;
demo.Func2();
}

void TestDemo::TestMockFunc()
{
// fun返回值是10,Mock返回值是1000,而此处打印100,
// 所以从打印信息来看,证明mock是成功的。
cout << fun() << endl;
}


4、main.cpp

//main.cpp
#include "TestCase.h"
#include "TestParam.h"

int main(int argc, char * argv[])
{
TestParam::GetInstance()->Init(argv[0]);

TestCase::RunAllUnit();

TestCase::RunClassUnit("TestDemo");

TestCase::RunSingleUnit("TestDemo", "TestFunc1");

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: