Yesterday I started to poke around with the cxxtest testing framework.
So basically, to start right off you need:
- Source file to test. It shouldn’t have a main function. At least if you do things the way I did which is by linking the compiled object file with the test runner object file (which defines a main function).
- A header file which defines the actual test.
- A Makefile that defines the rules needed to compile all of this.
Source files to test
So this is the simple source code that I am going to test (Yes I know it’s not a parser, I just named it that way, cause it’s cool):
#include "parser.hpp"
inline bool Parser::surrounded_by(const string& s, char left, char right)
{
return s.at(0)==left && s.at(s.length()-1)==right;
}
void Parser::strip(string& s) {
static const string filter="\r\n\t ";
size_t right = s.find_last_not_of(filter);
if(string::npos == right) {
s.clear();
return;
}
size_t left = s.find_first_not_of(filter);
if(string::npos == left) left=0;
s=s.substr(left,(right-left)+1);
}
And this is the header file:
#ifndef PARSER_HPP_
#define PARSER_HPP_
#include <string>
#include <map>
using std::map;
using std::string;
namespace Parser {
inline bool surrounded_by(const string& s, char left, char right);
void strip(string& s);
}
#endif
The actual test
I like things neatly tucked into directories so I made a subdir test. There I have a file named parserTest.h with the following content
Note: I don’t know why but WordPress displays the NULL character \ 0 (lines 51 and 54) as a space, so I wrote 0 instead.
#include <cxxtest/TestSuite.h>
#include "parser.hpp"
using std::string;
class ParserTestSuite : public CxxTest::TestSuite {
public:
void testStrip()
{
string s=" strip me ";
Parser::strip(s);
TS_ASSERT_EQUALS(s,"strip me");
}
void testNoStrip()
{
string s="don't strip me", s1=s;
Parser::strip(s);
TS_ASSERT_EQUALS(s,s1);
}
void testStripLeft()
{
string s=" left strip me";
Parser::strip(s);
TS_ASSERT_EQUALS(s,"left strip me");
}
void testStripRight()
{
string s="right strip me ";
Parser::strip(s);
TS_ASSERT_EQUALS(s,"right strip me");
}
void testSurrondedBy()
{
const string s="?am I surronded by questionmarks?";
TS_ASSERT( Parser::surrounded_by(s, '?', '?') );
}
void testNotSurroundedBy() {
const string s="I'm the authority";
TS_ASSERT( !Parser::surrounded_by(s, ' ', ' ') );
}
void testLeftOnlySurrondedBy() {
const string s="?Left yes but right no.";
TS_ASSERT( !Parser::surrounded_by(s, '?','!') );
}
void testRightOnlySurroundedBy() {
const string s="Left no but right yes!";
TS_ASSERT( !Parser::surrounded_by(s, '?', '!') );
}
void testNotSurroundedByNullChar() {
const string s="Am I terminated by a null char? Are we all?";
TS_ASSERT( !Parser::surrounded_by(s, 'A', 0) );
}
void testCStringNotSurroundedByNullChar() {
TS_ASSERT( !Parser::surrounded_by("A C string", 'A', 0) );
}
void testCStringSurroundedBy() {
TS_ASSERT( Parser::surrounded_by("! Yes !", '!', '!') );
}
};
Of course this blog entry is just an example of how to basically use cxxtest. So you should check out the manual (html | pdf) (as always) to see much more info. Also in the cxxtest source tree there is a directory sample that holds Makefilse for different operating systems and a few basic tests. The main difference between my sample and the ones in the source code tree of cxxtest is that I have subdirectory that holds the tests, hence the Makefiile is a bit different (also, I’ve stripped some of it that I don’t need, like GUI output).
The Makefile
So, the Makefile is really important. Just put it in test/Makefile. Of course, you should edit the paths in the SHELL and TESTGEN directory. The TESTGEN variable holds the path to the Perl script that generates the test runner cpp file (called error_printer here) which is later compiled and run.
SHELL = /bin/bash VPATH = ..:. CXXC = g++ -Wall -I. -I.. CC = g++ -c -I. -I.. TESTGEN = /usr/bin/cxxtestgen.pl TARGETS = error_printer TESTS = *.h all: $(TARGETS) clean: rm -f *~ *.o *.obj $(TARGETS) $(GUI_TARGETS) rm -f tests.cpp error_printer.cpp rm -f qt_runner.cpp distclean: clean rm -f Makefile run: error_printer ./error_printer error_printer.cpp: $(TESTS) $(TESTGEN) -o $@ --error-printer $(TESTS) stdio_printer.cpp: $(TESTS) parserTest $(TESTGEN) -o $@ --runner=StdioPrinter $(TESTS) tests.cpp: $(TESTS) $(TESTGEN) -o $@ $(TESTS) error_printer.o: error_printer.cpp $(CC) error_printer.cpp -o error_printer.o error_printer: error_printer.o parser.o $(CXXC) error_printer.o parser.o -o error_printer %: %.cpp $(CXXC) -o $@ $<
Now to run the tests we do
make run
in the test directory.
The output is:
[emil@lappy test]$ make run /usr/bin/cxxtestgen.pl -o error_printer.cpp --error-printer *.h g++ -c -I. -I.. error_printer.cpp -o error_printer.o g++ -c -o parser.o ../parser.cpp g++ -Wall -I. -I.. error_printer.o parser.o -o error_printer ./error_printer Running 11 tests.........OK!