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

一个简单的测试框架,代码简洁,不需要安装。

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

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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值