Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Unit Testing Framework for eVC++ Applications

0.00/5 (No votes)
17 Oct 2004 3  
A unit testing framework for eVC++ applications, and its usage.

Introduction

First of all, please forgive me for my bad English if it annoys you. Few months ago, I came to know about CPP Unit which is a unit testing framework for C++ application. I am a fresh guy out of college last year and was unaware about testing frameworks, so it was a very good experience for me to work with CPP Unit. As I was also programming on Windows CE, there was a need to write test cases for those applications also. But the problem with CPP Unit is that I was not able to compile for Windows CE as it used some features of C++ which are not supported by eVC++ compiler. I tried to search a similar test framework for Windows CE but I didn't find it. So after prolonged searching, I thought to write my own unit testing framework for Windows CE based applications. And this is what I came up with and I want to share with all.

This is a unit testing framework for applications written for Windows CE in particular. It provides GUI to select and run test cases, and to show results. It also stores the results of tests run to a file, if user wants.

Background

Programming in C++ for WinCE is very restrictive. It does not support templates, RTTI and other stuff of C++. CPP Unit uses templates and RTTI, but in eVC++ it's not available. That's why I have used lots of member function pointers and call backs to implement it.

Using the Frame Work

Compile the WCEUnit project to make WCEUnit.dll file for a particular platform. In order to use the testing framework, the test case project must be linked with the WCEUnit.dll. Write your test cases and link the project to WCEUnit.dll. Following topics discuss how to write test cases.

Writing Test Cases

When you write a test class, it should be derived from CTestSuite class defined in the framework. The CTestSuite class holds all the information about the test cases written for that particular suite. The individual test cases should be added to the test suite as show in the following example. The macros which are used in the following example are defined in the WCEMacros.h header file. These macros wrap the test case addition mechanism. The below code shows a test suite class written for testing CComplex class. The CComplex class is very simple, and does primary implementation of complex numbers, and I will not go into the details of that one.

//TestComplex.h

#ifndef __TEST_COMPLEX_INCLUDED__
#define __TEST_COMPLEX_INCLUDED__

#include "..\WCEUnit\TestSuite.h"
#include "..\WCEUnit\WCEUnitMacros.h"
#include "Complex.h"

class CTestComplex : public CTestSuite
{
public:
    //Pass the name of the test class as parameter.
    WCEUNIT_TESTSUITE_INIT(CTestComplex);
    //Pass the name of the test case function as parameter.
    WCEUNIT_ADD_TESTCASE(testAdd);
    WCEUNIT_ADD_TESTCASE(testEqual);
    WCEUNIT_ADD_TESTCASE_EXCEPTION(testExcp, CUserException);
    WCEUNIT_TESTSUITE_END();

public:
    /***********************************************************
    The signature of test case function should be void (func)();
    ***********************************************************/
    void testAdd();
    void testEqual();
    void testException();
    void CleanUpTest();
    void InitializeTest(); 
private:
    CComplex*        m_p1;
    Ccomples*        m_p2;
    CComplex*        m_p3;
};
#endif //__TEST_COMPLEX_INCLUDED__

Macro description

  • WCEUNIT_TESTSUITE_INIT(testSuite)

    Pass test suite class name to it. This function defines the starting of a CreateTestSuite() function. This function is defined as pure virtual in the CTestSuite class and is used to create the test case lists.

  • WCEUNIT_ADD_TESTCASE(testCase)

    This macro adds the code to the CreateTestSuite() function to add a test case function.

  • WCEUNIT_ADD_TESTCASE_EXCEPTION(testCase, testException)

    This macro is used to add a special type of test case function which should be tested for a particular type of exception. If the test case does not throw the exception then it is considered to fail.

  • WCEUNIT_TESTSUITE_END()

    This macro ends the CreteTestSuite() function.

You can see functions InitializeTest() and CleanUpTest() in the definition of the class. These functions are called before and after every test case execution. From the UI, you can select the behavior about these functions, i.e., whether you want those functions to be called for every test case or only once for all the test cases for a particular test suite class. You can specify your choice by checking or un-checking the Init Once check box on the main dialog.

Writing Test Conditions

While writing test cases, use macros defined in the WCEUnitMacros.h for writing the test conditions. Following are the macros used to write test conditions:

  • WCEUNIT_ASSERT(condition)

    If the condition is evaluated to false then it asserts and marks the test as failed.

  • WCEUNIT_ASSERT_FAIL(condition)

    This is reverse of above macro. If you want that the condition must return false then use this macro to test the condition.

  • WCEUNIT_ASSERT_EQUALS(testValue, compValue)

    This macro asserts if the testValue and compValue are not equal. If the two values are not equal then it marks the test case as failed. If you are using some user defined class objects to compare then the class must have '==' operator overloaded.

  • WCEUNIT_DOUBLE_EQUALS(testValue, compValue)

    This macro is similar to the above one but it assumes that the testValue and compValue are convertible to a double value. It type casts the results into a double value and then compares.

  • WCEUNIT_DOUBLE_BETWEEN(testValue, minValue, maxValue)

    This macro tests weather the testValue is less than maxValue and greater than minValue. It is assumed that the three values of expressions are evaluated to double. The following code shows the usage of the above macros.

Following code shows the use of above macros in test cases for CComplex class.

void CTestComplex::testAdd()
{
    CComplex temp;
    temp = *m_p1 + *m_p2;
    WCEUNIT_ASSERT(CComplex(30, 70) == temp);
}

void CTestComplex::testEqual()
{
    CComplex temp2;            //Below test should fail.
    WCEUNIT_ASSERT_FAIL(temp2 == *m_p3);

    CComplex temp1;            //Below test should pass.
    temp1 = *m_p1 + *m_p2;
    WCEUNIT_ASSERT_EQUALS(temp1, *m_p3);

    //To check the equality of the real and imaginary members.
    WCEUNIT_DOUBLE_EQUALS(temp1.GetReal(), m_p3->GetReal());
    WCEUNIT_DOUBLE_EQUALS(temp1.GetImaginary(), m_p3->GetImaginary());
}

void CTestComplex::testException()
{
    //This fuction is only to demonstrate the exception test.
    //It throws CUserException. This test will fail as the expected exception
    //is CFileException.

    CUserException e;
    THROW(&e);
}

void CTestComplex::InitializeTest()
{
    m_p1 = new CComplex(10, 20);
    m_p2 = new CComplex(20, 50);
    m_p3 = new CComplex(30, 70);
}

void CTestComplex::CleanUpTest()
{
    delete m_p1;
    delete m_p2;
    delete m_p3;
}

Running Test Cases

The CTestRunner class is responsible for running the test cases and getting the test results. To run the test cases you have written, create an instance of CTestRunner class. Also create the instance of all the test suite classes, and pass the address of each in the AddTestSuite() function of CTestRunner class. Then call Start() function of CTestRunner. The following code shows how to run test cases:

BOOL CTestComplexApp::InitInstance()
{
    //Create the instance of CTestRunner class.
    CTestRunner obj;
    //Create the instance of CTestComplex test suite class.
    CTestComplex test1;

    //Pass the address of CTestComplex instance to the AddTestSuite() function.
    obj.AddTestSuite(&test1);

    //Sart the WCEUnit dialog to run the test cases.
    obj.Start();
    return FALSE;
}

The GUI

WCEUnit/wceunit.jpg

The above image shows the main dialog. To select a test case to run, click the Select button, a new dialog will appear with the tree view of the test cases written by you. You can either select single test case or a test suite or All Tests. If you want to run all the test cases for a particular test suite then select that test suite, or if you want to run all the test cases of all the test suites then select the root node All Tests. The check box Init once is given for the option when the InitializeTest() and CleanUpTest() will be called. If it is checked then for each test suite, the IntializeTest() and CleanUpTest() is called only once respectively, before and after all the test cases are executed. If it is unchecked then for each and every test case, these two functions are called. Click the Run button to run the selected test cases. The results are displayed in the list box. If you want to see more information then just click the result item, you will be shown the detailed result. If you want to store the test results in a text file then check the Make log check box, a file \\WCEUnitLog.txt is created with the detailed test result information.

Points of Interest

During the development of this project, I learned a lot about the pointer to member functions in C++. They are amazing things and do a lot more than what you can imagine. May be next time, I will try to post some thing about pointer to member functions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here