一个精简的测试框架及使用示例
2012-09-03 11:46
495 查看
一个简单的测试框架,代码简洁,不需要安装。
下面是其源码以及使用示例:
1、testframe文件夹
mock.h
TestCase.h
TestCase.cpp
TestParam.h
TestParam.cpp
2、src文件夹
Demo.h
Demo.cpp
3、case文件夹
TestDemo.h
TestDemo.cpp
4、main.cpp
下面是其源码以及使用示例:
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; }
相关文章推荐
- 使用CPPUNIT如何建立一个基于MFC的GUI测试框架
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
- [转]使用CPPUNIT如何建立一个基于MFC的GUI测试框架
- 使用Python的Bottle框架写一个简单的服务接口的示例
- 使用Python的Bottle框架写一个简单的服务接口的示例
- 一个超级简单的HTML模板框架源代码以及使用示例
- Xqk.Data数据框架使用说明之:一个简单的示例
- 一个简短的epoll服务器示例, 监听5000个端口, 使用线程池
- .net 简单图表控件 (介绍测试示例使用部分) [b/s应用程序控件] I
- 细数Android Studio中使用junit4测试框架中的坑
- 使用脚本设计自动化测试框架的原则
- TestNg测试框架使用笔记
- Google C++测试框架系列入门篇:第一章 介绍:为什么使用GTest?
- Yii2框架中使用PHPExcel导出Excel文件的示例
- 一个用于每一天JavaScript示例-使用缓存计算(memoization)为了提高应用程序性能
- RxStore_一个使用RxJava的轻量级的数据持久化框架(二)
- 怎样从0开始搭建一个测试框架_11——支持方法
- 使用Nancy和Simple.Data两个轻量级的框架打造一个分布式开发系统(一)
- 使用selenium进行web测试项目框架
- [SoapUI] 从上一个测试步骤获取ID list,通过Groovy脚本动态生成 Data Source 供后面的步骤使用