C++ – Unit test – easy, one file, basic, simple
I few weeks ago I wanted to use unit test. But when I was searching for a framework easy to use, fast, that do not need to be installed 10 libs to make it working etc… Well I did not find anything that satisfy me.
So I make my own lib.
My lib is just a include file. It works every where, it is fast, it compiles in one line, you do not need any doc. But, of course, it is useful to make simple case test!! It do not replace real test framework. But it is a good option to make a test in a few minutes just to be sure every thing is okay.
The code
Here is the include you need (you just need this file).
#ifndef UTESTER_HPP
#define UTESTER_HPP
// /!\ Please, you must read the license at the bottom of this page
#include <iostream>
#include <list>
#include <string>
#include <stdio.h>
#define TestClass(X)\
int main(void){\
X Controller;\
Controller.Run();\
return 0;\
}\
/**
* @author Berenger Bramas (berenger.bramas@inria.fr)
* Please read the license
*
* This class is used to make simple unit test cases
*
* Please refer to testUTest.cpp to see an example
* @warning Create a derived class that implement SetTests() and use TestClass() macro
*/
template <class TestClass>
class FUTester{
// Test function pointer
typedef void (TestClass::*TestFunc)(void);
/** Test descriptor */
struct TestFuncDescriptor{
TestFunc func; //< Test adress
std::string name; //< Test name
};
std::list<TestFuncDescriptor> tests; //< all tests
int totalTests; //< number of tests
int currentTest; //< current processing test in the run
int currentStep; //< current processing step in the run
int failledSteps; //< number of failled step in the current test
int failledTests; //< number of failled tests
protected:
/** Constructor */
FUTester(){
totalTests = 0;
}
/** Callback before processing test */
virtual void Before(){}
/** Callback after processing test */
virtual void After(){}
/** Callback before each unit test */
virtual void PreTest(){}
/** Callback after each unit test */
virtual void PostTest(){}
/**
* This function has to add tests
* <code> AddTest(&MyTest::TestOne); </code>
*/
virtual void SetTests() = 0;
/**
* Add a test without giving a name
* @param inFunc test function address
*/
void AddTest(TestFunc inFunc){
char buff[256];
sprintf(buff,"Unamed Test number %d",totalTests+1);
AddTest(inFunc,buff);
}
/**
* Add a test with a name
* @param inFunc test function address
* @param inFuncName function name
*/
void AddTest(TestFunc inFunc, const std::string& inFuncName){
++totalTests;
TestFuncDescriptor desc;
desc.func = inFunc;
desc.name = inFuncName;
tests.push_back(desc);
}
/**
* To print a message manually in the test
* @param value a object that ostream can work on
*/
template <class Output>
void Print(const Output& value){
std::cout<< "--- Output from program : " << value << "\n";
}
/**
* To test
* @param result the test result
* if result is false test failled
*/
void assert(const bool result){
++currentStep;
if(!result){
std::cout << ">> Step " << currentStep << " Failled\n";
++failledSteps;
}
}
/**
* To test equality
* @param v1 value one
* @param v2 value 2
* if v1 is not equal v2 test failled
*/
template <class T>
void equal(const T& v1, const T& v2){
assert(v1 == v2);
}
/**
* To test equality
* @param v1 value one
* @param v2 value 2
* if v1 is equal v2 test failled
*/
template <class T>
void different(const T& v1, const T& v2){
assert(v1 != v2);
}
public :
/**
* Processing the test
*/
void Run(){
tests.clear();
// register tests
SetTests();
TestClass* const toTest = static_cast<TestClass*>(this);
currentTest = 0;
failledTests = 0;
Before();
// for each tests
const typename std::list<TestFuncDescriptor>::const_iterator end = tests.end();
for(typename std::list<TestFuncDescriptor>::iterator iter = tests.begin() ; iter != end ; ++iter){
currentStep = 0;
failledSteps = 0;
std::cout << "[Start] " << (*iter).name << "\n";
PreTest();
TestFunc ff = (*iter).func;
(toTest->*ff)();
PostTest();
if(failledSteps){
std::cout << "[Finished] FAILLED (" << failledSteps << "/" << currentStep<< " steps failled)\n";
++failledTests;
}
else{
std::cout << "[Finished] PASSED (" << currentStep << " steps)\n";
}
++currentTest;
}
After();
std::cout <<"Test is over, " << (totalTests-failledTests) << " Passed, " << failledTests << " Failled\n";
}
};
#endif
To make the things working you have to :
- inherit from the test class (with your class as template)
- implement SetTests
- add the macro TestClass(your class) at the end of your file
An example
#include "FUTester.hpp"
// compile by g++ utestTest.cpp -o utestTest.exe
/** this class show a simple example of unit test */
class MyTest : public FUTester<MyTest> {
void Before(){
Print("Before running the test");
}
void TestOne(){
assert(true);
assert(false);
assert(1 == 1);
}
void TestTwo(){
equal(1 , 1);
different(1 , 1);
}
void After(){
Print("After running the test");
}
void PreTest(){
Print("Before each test");
}
void PostTest(){
Print("After each test");
}
// You must implement it
void SetTests(){
AddTest(&MyTest::TestOne);
AddTest(&MyTest::TestTwo,"My Second Test");
}
};
// You must do this
TestClass(MyTest)
Output :
./utestTest.exe --- Output from program : Before running the test [Start] Unamed Test number 1 --- Output from program : Before each test >> Step 2 Failled --- Output from program : After each test [Finished] FAILLED (1/3 steps failled) [Start] My Second Test --- Output from program : Before each test >> Step 2 Failled --- Output from program : After each test [Finished] FAILLED (1/2 steps failled) --- Output from program : After running the test Test is over, 0 Passed, 2 Failled
Another Example
#include "FUTester.hpp"
#include "../Sources/Containers/FList.hpp"
// compile by g++ utestList.cpp -o utestList.exe
/**
* This file is a unit test for the FList class
*/
/**
* This class is simply used to count alloc dealloc
*/
class TestObject{
public:
static int counter;
static int dealloced;
TestObject(){
++counter;
}
TestObject(const TestObject&){
++counter;
}
~TestObject(){
++dealloced;
}
};
int TestObject::counter(0);
int TestObject::dealloced(0);
/** this class test the list container */
class TestList : public FUTester<TestList> {
// Called before each test : simply set counter to 0
void PreTest(){
TestObject::counter = 0;
TestObject::dealloced = 0;
}
// test size
void TestSize(){
FList<TestObject> list;
list.pushFront(TestObject());
list.pushFront(TestObject());
list.pushFront(TestObject());
assert(list.getSize() == 3);
assert((TestObject::counter - TestObject::dealloced) == list.getSize());
list.clear();
assert(list.getSize() == 0);
assert(TestObject::counter == TestObject::dealloced);
}
// test copy
void TestCopy(){
FList<TestObject> list;
list.pushFront(TestObject());
list.pushFront(TestObject());
list.pushFront(TestObject());
FList<TestObject> list2 = list;
assert(list.getSize() == list2.getSize());
assert((TestObject::counter - TestObject::dealloced) == (list.getSize() + list2.getSize()));
}
// test iter
void TestIter(){
FList<TestObject> list;
{
FList<TestObject>::BasicIterator iter(list);
assert(!iter.isValide());
}
{
list.pushFront(TestObject());
list.pushFront(TestObject());
list.pushFront(TestObject());
FList<TestObject>::BasicIterator iter(list);
assert(iter.isValide());
int counter = 0;
while(iter.isValide()){ iter.progress(); ++counter; }
assert(!iter.isValide());
assert(counter == list.getSize());
}
}
// set test
void SetTests(){
AddTest(&TestList::TestSize,"Test Size");
AddTest(&TestList::TestCopy,"Test Copy");
AddTest(&TestList::TestIter,"Test Iter");
}
};
// You must do this
TestClass(TestList)
Output :
./utestList.exe [Start] Test Size [Finished] PASSED (4 steps) [Start] Test Copy [Finished] PASSED (2 steps) [Start] Test Iter [Finished] PASSED (4 steps) Test is over, 3 Passed, 0 Failled
Licence
It is under LGPL.
Subscribe to the RSS feed and have all new posts delivered straight to you.
