Hello,
I've written a small UnitTest model (I know there are libraries out there that implement UnitTesting for C++, however I wanna do my own). Here is the code, the documentation within comments should be conclusive (however if it's not, please post here a reply so I can elaborate):
Tester.hpp#ifndef TEST_TESTER_HPP
#define TEST_TESTER_HPP
#include <string>
// Contains all Tester classes including a TestAll class which runs tests on any or all classes.
namespace Test{
// Represents the abstract model of a tester class.
// Tester classes are used to test other, particular, classes or methods.
class Tester{
private:
// Used to identify the class or method tested.
// should have the format: test_<className>[<_methodName>].
std::string _name;
protected:
// Creates a new tester with the specified name.
Tester(const std::string& name): _name(name) { }
public:
// Enables polymorphic destruction of derived type instances.
virtual ~Tester() { }
// Runs a test, returns the number of errors.
virtual unsigned int test() = 0;
// Returns the test name (see _name member description).
const std::string& getName() const {return _name;}
};
}
#endif
TestShape.hpp (doesn't do any actual testing yet, just an example of a specialized Tester)
#ifndef TEST_TESTSHAPE_HPP
#define TEST_TESTSHAPE_HPP
#include "Tester.hpp"
#include "Shape.hpp"
namespace Test{
// Represents a Tester specialized for testing the Shape class within the Tetrix::Domain namespace.
class TestShape : public Test::Tester{
public:
// Creates a new Tester for testing the Shape class.
TestShape(): Test::Tester("test_Shape") { }
// Runs tests on the Shape and returns the number of erros for a Shape object.
// Altho incorrect to have massive inline functions,
// it is better to have 1 file per test function.
virtual unsigned int test(){
return 1;
}
};
}
#endif
TestAll.hpp#ifndef TEST_TESTALL_HPP
#define TEST_TESTALL_HPP
#include <vector>
#include <iostream>
#include "Tester.hpp"
// Include testers here
#include "TestShape.hpp"
namespace Test{
// Represents a special Tester, it is responsible with running tests from all available test classes.
class TestAll : public Test::Tester{
private:
// Retains a vector of references to all available testers.
std::vector<Test::Tester*> _forTests;
// Runs a test from the Tester, returns the number of errors and
// outputs to stdout if the test failed.
unsigned int _test(Test::Tester* tester);
// Outpust to stdout the name of the tester and the number of errors specified.
void _testFailed(const Test::Tester* const tester, int errors){
std::cout<<tester->getName()<<" failed with "<<errors<<" errors"<<std::endl;
}
public:
// Creates a new TestAll instance.
TestAll();
// Destroies the current instance. DO NOT CALL EXPLICITLY!
// Enables polymorphic destruction of derived type instances.
virtual ~TestAll();
// Runs all available tests and returns the error count.
virtual unsigned int test();
};
}
#endif
TestAll.cpp (there are 3 Testers of the same type to test the noTest array)
// Array of indexes for Testers that will not run tests.
// Index + 10 to get the line where the respective Tester is being added,
// e.g: 0 + 10 => Test::TestShape, on index 0 Test::TestShape tester is found.
// The array elements must be separated with commas or the code fails. Use -1 to test everything.
#define DO_NOT_TEST -1
#include "TestAll.hpp"
Test::TestAll::TestAll(): Test::Tester::Tester("test_TestAll"), _forTests(std::vector<Test::Tester*>()){
_forTests.push_back(new Test::TestShape);
}
Test::TestAll::~TestAll(){
auto end = _forTests.end();
for (auto it = _forTests.begin(); it != end; it++)
delete *it;
}
unsigned int Test::TestAll::test(){
std::vector<unsigned int> noTest({DO_NOT_TEST});
unsigned int totalErrors = 0,
testSize = _forTests.size(), noTestSize = noTest.size();
for (unsigned int testIndex = 0, noTestIndex = 0; testIndex != testSize; testIndex++)
if (noTestIndex < noTestSize && noTest[noTestIndex] == testIndex)
noTestIndex++;
else
totalErrors += _test(_forTests[testIndex]);
if (totalErrors != 0)
_testFailed(this, totalErrors);
return totalErrors;
}
unsigned int Test::TestAll::_test(Test::Tester* tester){
if (unsigned int errors = tester->test() != 0){
_testFailed(tester, errors);
return errors;
}
else
return 0;
}
main.cpp#define DO_TESTS
#ifndef DO_TESTS
#include "Application.hpp"
int main(){
Tetrix::Application app
return app.run();
}
#else
#include "TestAll.hpp"
int main(){
Test::TestAll testAll;
testAll.test();
return 0;
}
#endif
What do you guys think about this UnitTesting method? Is it good? Is it bad? Does it miss anything? Does it have to much of something?
[edit]
Fixed a bug in the Test::TestAll::test() for loop. The conditions at if statement were incorrect.
[/edit]