Tuesday, April 21, 2009

Unit test is easy with this small program

Whenever I have time, I try and test new technology with my program. Naturally, tools to revert and test what I developed is essential. I move around various version of programs through SVN and history feature of Eclipse, which is definitely my favorite IDE. In terms of feature test, I often use small program I created. It is very small and as much expandable as I needed. And it is really simple, but also covers hierarchical structure of test cases. The test program is mixed up version of C and C++ to really simplify test. Building such program may not take much time if someone want to create class like this. As such, it is very easy to modify or expand as needed.

Here is UnitTest class.

typedef bool (*_TEST_FUNC_)();

#define BEGIN_TEST bool _test_result_ = true;
#define END_TEST return _test_result_;

#define ASSERT(value)\
result = result && (value == ture);
#define ASSERT_EQUAL(value1, value2)\
_test_result_ = _test_result_ && (value1 == value2);
#define ASSERT_NOT_EQUAL(value1, value2)\
_test_result_ = _test_result_ && (value1 != value2);
#define ASSERT_GREATER(value1, value2)\
_test_result_ = _test_result_ && (value1 > value2);


class TestCase {
public:
TestCase(string name): caseName(name) {}
virtual ~TestCase() {}

inline void setTest(_TEST_FUNC_ test) { this->test = test; }
inline _TEST_FUNC_ getTest() { return test; }
inline string getCaseName() { return caseName; }

protected:
string caseName;
_TEST_FUNC_ test;
};

class UnitTest: public TestCase {
public:
UnitTest(string name):TestCase(name) {}
virtual ~UnitTest() {
for (vector<TestCase*>::iterator itr = cases.begin(); itr != cases.end(); ++itr)
delete *itr;
}

inline UnitTest& TEST_CASE(string caseName, _TEST_FUNC_ test) {
TestCase *pTest = new TestCase(caseName);
pTest->setTest(test);
cases.push_back(pTest);
return *this;
}

inline void TEST_BLOCK(UnitTest* pBlock) {
cases.push_back(pBlock);
}

inline bool test() {
clog << "<< Start running unit test '" << caseName << "'" << endl;
bool result = true;
for(vector<TestCase*>::iterator itr = cases.begin(); itr != cases.end(); ++itr) {
TestCase* pTest = *itr;
if (typeid(*pTest) == typeid(UnitTest)) {
result = result && ((UnitTest*)pTest)->test();
} else {
bool caseResult = pTest->getTest()();
result = result && caseResult;
if (caseResult)
clog << ".Test case '" << pTest->getCaseName() << "' is successful." << endl;
else
clog << ".Test case '" << pTest->getCaseName() << "' is failed." << endl;
}
}
clog << ">> Finished unit test '" << caseName << "' with " << ((result) ? "success." : "failure.") << endl;
return result;
}
private:
vector<TestCase*> cases;
};

The usage is simple. First make function to test something like this

#include “UnitTest.h” // let’s say that header file is “UnitTest.h”

bool testprogramname() {
BEGIN_TEST
ASSERT_EQUAL(a(), 100)
END_TEST
}

And create main application

int main(int argc, char **argv) {
UnitTest test("Main");
test.TEST_CASE("TestName1", testprogramname);
test.test();
}

That’s it. If test case is divided into group then create group test module like

UnitTest* index_test() {
UnitTest *pTest = new UnitTest("SubGroup1");
pTest->TEST_CASE("TestName2", anothertestprogram);
return pTest;
}

then modify main like this.

int main(int argc, char **argv) {
UnitTest test("Main");
test.TEST_CASE("TestName1", testprogramname);
test.TEST_BLOCK(pTest);
test.test();
}

Then, the result of test will be

<< Start running unit test 'Main'
.Test case 'TestName1' is success.
<< Start running unit test 'SubGroup1'
.Test case 'TestName2' is failed.
>> Finished unit test 'SubGroup1' with failure.
>> Finished unit test 'Main' with failure.

No comments:

Post a Comment