This document can be considered a "port" of the JUnit Guide I wrote. In particular, this document talks about using CppUnit in Solaris e.g. in sun450 (although it should also work for other Unix variants such as Linux). As in the JUnit Guide, I have included only the bare minimum to get you started. I will first go through the installation of CppUnit in the next section, followed by a description of how to use CppUnit using a sample program. Next, I will suggest some ways of organizing your project and test codes before ending the document with pointers to some useful references.
cd /home/student/engpk gunzip UnixCppUnit.tar.gz tar -xvf UnixCppUnit.tarThe above three commands will create a new directory called UnixCppUnit in /home/student/engpk.
cd /home/student/engpk/UnixCppUnit makeAfter the compilation, you should have the following in /home/student/engpk/UnixCppUnit/lib:
-rwx------ 1 engpk compsc 1248528 libCppUnit.so* -rwx------ 1 engpk compsc 1200508 libCppUnitRunner.so* -rwx------ 1 engpk compsc 1215800 libCppUnitTest.so*Note: If you're compiling on Linux, the compilation will fail. What you need to do is to modify the file Make.rules in the config subdirectory before executing the make command. Look for the line starting with ARFLAGS in the file Make.rules and change the -G option to -shared on the same line and the compilation will be successful.
cd /home/student/engpk/UnixCppUnit/include cp ../test/framework/*.h . cp ../test/textui/*.h .
cd /home/student/engpk/UnixCppUnit/samplesYou will see a file called test which is an executable program. Unfortunately, "test" is also a builtin command of the bash shell which most of you are running when you logon to our systems. Therefore, you need to rename this file before running it:
mv test testprog ./testprog ExampleTestCaseWhen you run the program testprog, you will see an error message:
ld.so.1: testprog: fatal: libCppUnit.so: open failed: No such file or \ directory KilledThis is because the linker is unable to find the CppUnit shared library. What you should do now is to set the LD_LIBRARY_PATH environment variable (enter the following as a single line):
export LD_LIBRARY_PATH= $LD_LIBRARY_PATH:/home/student/engpk/UnixCppUnit/libYou may want to put the above line in your .bash_profile or .profile so that the library path is always set when you logon to one of the main servers. Now, try running the sample program again:
./testprog ExampleTestCaseYou will see some failure messages - it's okie. This is because the sample program creates a few failed tests:
..F .F !!!FAILURES!!! Test Results: Run: 3 Failures: 2 Errors: 0 There were 2 failures: 1) line: 28 ExampleTestCase.cpp "result == 6.0" 2) line: 50 ExampleTestCase.cpp "expected: 12 but was: 13"If you got the above message, then CppUnit has been installed properly. You are now ready to use CppUnit for your development work.
Assuming that you want to test a class called Parser. The following are the general steps to use the CppUnit framework to test this class:
#ifndef Course_h #define Course_h #include <string> class Course { public: // Default constructor Course(); // Constructor Course(std::string nm, int gr); // method to get the name of the course std::string getCourseName(); // method to get the grade of the course int getCourseGrade(); private: std::string course_name; // name of this course int grade; // grade of this course }; #endif
#include "Course.h" // default constructor Course::Course() { course_name = ""; grade = -1; } // constructor Course::Course(std::string nm, int gr):course_name(nm) { grade = gr; } // method to get the name of the course std::string Course::getCourseName() { return course_name; } // method to get the grade of the course int Course::getCourseGrade() { return grade; }
#ifndef Student_h #define Student_h #include <iostream> #include <string> #include "Course.h" const int MAXNUM = 20; // Maximum number of courses allowed per student class Student { public : // Constructor Student(std::string nm, std::string no); // Method to return student's name std::string getStuName(); // Method to return student's number std::string getStuNumber(); // Method to assign a grade to a course void assignGrade(std::string co, int gr); // Method to return the grade of a course int getGrade(std::string co); private: std::string name; // name of the student std::string number; // the student's number Course course_grades[MAXNUM]; // courses taken by student int no_of_courses; // the current number of courses taken }; #endif
#include "Student.h" // Constructor Student::Student(std::string nm, std::string no):name(nm), number(no) { no_of_courses = 0; } // Method to return student's name std::string Student::getStuName() { return name; } // Method to return student's number std::string Student::getStuNumber() { return number; } // Method to assign a grade to course void Student::assignGrade(std::string co, int gr) { // check whether the maximum number of courses have been taken if (no_of_courses == MAXNUM) { std::cout << "You have exceeded the maximum number of courses !\n"; return; } // create a new course Course c(co, gr); course_grades[no_of_courses++] = c; } // Method to return the grade of a course int Student::getGrade(std::string co) { int i = 0; while (i < no_of_courses) { //check if course name the same as co if (course_grades[i].getCourseName() == co) return (course_grades[i].getCourseGrade()); i++; } return(-1); }
cd /home/student/engpk mkdir cs3214sNow, compile the files as follows:
g++ -c Course.cpp g++ -c Student.cppYou will see two files - Course.o and Student.o created in the directory. Just leave them alone for the moment. Next, create the test class. The following are the test files I wrote for the Student class (called TestStudent.h and TestStudent.cpp):
#ifndef TestStudent_h #define TestStudent_h #include <iostream> #include <string> // Note 1 #include "TestCase.h" #include "TestSuite.h" #include "TestCaller.h" #include "TestRunner.h" #include "Student.h" class StudentTestCase : public TestCase { // Note 2 public: // constructor - Note 3 StudentTestCase(std::string name) : TestCase(name) {} // method to test the constructor void testConstructor(); // method to test the assigning and retrieval of grades void testAssignAndRetrieveGrades(); // method to create a suite of tests static Test *suite (); }; #endif
#include "TestStudent.h" // method to test the constructor void StudentTestCase::testConstructor() { // Note 4 // create a student object Student stu("Tan Meng Chee", "94-1111B-13"); // check that the object is constructed correctly - Note 5 std::string student_name = stu.getStuName(); assert(student_name == "Tan Meng Chee"); std::string student_number = stu.getStuNumber(); assert(student_number == "94-1111B-13"); } // method to test the assigning and retrieval of grades void StudentTestCase::testAssignAndRetrieveGrades() { // create a student Student stu("Jimmy", "946302B"); // assign a few grades to this student stu.assignGrade("cs2102", 60); stu.assignGrade("cs2103", 70); stu.assignGrade("cs3214s", 80); // verify that the assignment is correct - Note 6 assertEquals(60, stu.getGrade("cs2102")); assertEquals(70, stu.getGrade("cs2103")); // attempt to retrieve a course that does not exist assertEquals(-1, stu.getGrade("cs21002")); } // method to create a suite of tests - Note 7 Test *StudentTestCase::suite () { TestSuite *testSuite = new TestSuite ("StudentTestCase"); // add the tests testSuite->addTest (new TestCaller("testConstructor", &StudentTestCase::testConstructor)); testSuite->addTest (new TestCaller ("testAssignAndRetrieveGrades", &StudentTestCase::testAssignAndRetrieveGrades)); return testSuite; } // the main method - Note 8 int main (int argc, char* argv[]) { if (argc != 2) { std::cout << "usage: tester name_of_class_being_test" << std::endl; exit(1); } TestRunner runner; runner.addTest(argv[1], StudentTestCase::suite()); runner.run(argc, argv); return 0; }
g++ -c TestStudent.cpp In file included from TestStudent.cpp:1: TestStudent.h:7: TestCase.h: No such file or directory TestStudent.h:8: TestSuite.h: No such file or directory TestStudent.h:9: TestCaller.h: No such file or directory TestStudent.h:10: TestRunner.h: No such file or directoryWhen you compile the test program, the above error message will appear. This is because the compiler is unable to locate the CppUnit header files. The correct way to compile this (assuming you store the CppUnit header files in /home/student/engpk/UnixCppUnit/include as I described earlier) is:
g++ -I/home/student/engpk/UnixCppUnit/include -c TestStudent.cppAt the end of this step, you would have TestStudent.o. Now, put everything together to create an executable test program (execute the following as a single command):
g++ -o tester Course.o Student.o TestStudent.o TestRunner.o \ -lCppUnit -lCppUnitRunnerThe above command will create the executable called tester.
g++ -L/home/student/engpk/UnixCppUnit/lib -o tester Course.o Student.o \ TestStudent.o TestRunner.o -lCppUnit -lCppUnitRunnerTo run the test suite, simply type:
tester TestStudentand you're off doing your unit testing :-)
// In Student.h under public // Method to return the average grade float findAveGrade(); // In Student.cpp // Method to return the average grade float Student::findAveGrade() { float sum = 0.0, average; // sum up the marks in all the courses for (int i = 0; i < no_of_courses; i++) sum += course_grades[i].getCourseGrade(); average = sum / no_of_courses; return(average); }Your job is to write a method in the TestStudent class to test this newly created method. Give it a try and see whether you really know how to use CppUnit ;-)
You should also learn how to make use of the utility make. Use make to do automatic compilation and integration of the code from various members. The process is non-trival and requires a lot of coordination between members. Hence, it is important that each team tie down among themselves a standard way of managing their own source code.