first steps with Claus Dręby's unit++
0. Intro
why am i writing this?
basically, when i thought i should start learning c++, i found there was so much new stuff to learn. i
had learned my programming with turbo pascal in a dos environment and then missed a few steps of the development.
so now i find myself in a flurry of buzzwords and i don't know where the handle is that will let me shake
all this into place.
writing this text helps me keep track of what i'm learning. if you are in a similar situation, then my hope
is you can profit from these notes. they reflect the help that i have received from kind people,
notably Claus who is a complete gem.
you should be prepared to spend some time with this. it is a keystroke-by-keystroke walk. you should really
follow those keystrokes one by one. so please read slowly even you find my prose rather verbose.
do not expect any immediate
progress in your current programming exercise. maybe you should think of this as some
guided time spent in a car simulator, before your first driving lesson.
you may safely assume that every mistake mentioned hereinafter (ah, lawyers!!) is one that i made myself.
i am certainly not god's gift to the programmer's guild, and it shows.
but then, i believe we all learn from mistakes, so i try to make plenty and then play intelligent
by not repeating them. the master level in this game is attained when you learn from other people's mistakes.
the implication is that mistakes need to be shared...
where to start ?
this text assumes that
- you are interested in c++ programmer, but not a wizard yet
- you heard about the benefits of unit testing and are willing to give it a try
-
- you have downloaded and installed the unit++ package by Claus Dręby
all examples i will give are based on the following:
- use of g++ version 2.95.3 Claus rightly pointed out to me that there are newer versions of g++ available that provide more ansi compliance etc. i myself will upgrade the whole system as soon as i find the time and this will take me to g++ 3.x also. for the moment, however, this is it: old.
- using g++ from the command line
- running unit++ in textmode
- libunit++.a resides in /lib
simply because this is the case on my system as i write.
what is going to happen ?
i will take you through two very elementary steps.
step 1: a very trivial first encounter
this serves the purpose of seeing the mechanics of unit++ without bothering about
any actual program you may wish to test later. the focus is really on getting to
know the tool that is now new in your box.
step 2: applying the lessons of step 1 in some toy project.
the hope is that after these two, you will beginn to see how stuff interacts, when to look in the
html documentation and what to find there, etc.
ready? let's go.
1. step 1: trying the new toy
just do it...
we start in the environment that i have outlined above. the first step tries to keep things really simple by
changing one thing at a time only. this changed thing is the unit++ library. thus, i don't want any
real application code for this. here is how.
-
edit a new file to contain the following text. do not copy / paste this, for it contains html tags that may cause havoc
with your compiler. also, do not type the line numbers.
1 #include <unit++.h>
2 #include <string>
3 using namespace std;
4 using namespace unitpp;
5 namespace {
6 class YourTest : public suite
7 {
8 void testEmptyString() {
9 string joe;
10 assert_eq("length check", (unsigned int) 0, joe.length() ); // parameter order: text, expected, actual
11 assert_eq("content check", "", joe);
12 }
13 public:
14 YourTest() : suite("YourFirstTest")
15 {
16 add("EmptyString", testcase(this, "TestEmptyString", &YourTest::testEmptyString));
17 suite::main().add("YourTestSuite", this);
18 }
19 };
20 * theTest = new YourTest();
21 }
- save this file as mytest.cpp or find it in a directory nearby
- compile/build as follows: g++ -o mytest mytest.cpp -lunit++ -Wall
- run the resulting executable: ./mytest
- observe the result. it should look like this:
Tests [Ok-Fail-Error]: [1-0-0]
if it doesn't, that just goes to show that bugs travel mysterious ways doesn't it?
may i humbly suggest you check for typing errors...
- that's it.
what's happening here?
clearly, this is not my invention at all. it is merely an adjusted copy/paste from the file unitpp.html from the documentation
of unit++. let's take it line by line and see what we can learn:
1 tell the system you are planning to use the fruits of Claus's work
2 why string? because i am going to write a test for the string class, which is simple enough even for my
feeble understanding ( a misunderstanding on my part, no doubt )
3, 4 namespaces are a c++ feature. unit++ has one of it's own. std is the namespace for pretty much anything
you would expect to find in a c++ environment. without the using clause for std, i would have to write std::string
instead of just string. it is a bit like using the same street name in different towns, i suppose...
5 this opens a so called anonymous name space. all the names defined now are valid exactly here and nowhere
else. so nobody has to be a genius about name inventing. YourTest might have been called Test and the line
cast in iron for all future applications.
6 the beginning of a class definition. it ends in line 19. the class inherits from suite.
suite is in unit++, so here you start using...
8-12: this is a test. it comes as a method in your class. it declares an object, namely the string called joe.
it also performs two checks on joe. the function called is part of unit++, and you are using again.
the type cast stops a warning (int and unsigned int compared)
13 why the test above is not public i do not understand as yet....
14-19 the constructor method for your class. pretty complicated as constructors go.
YourTest() : suite("YourFirstTest") based on the constructor for class suite, (no surprise)
16 the method testEmptyString is "registered" as a test case. more precisely, since the class is a suite, it
can have tests added to it. so, this line defines a test and adds it to the suite.
17 now this suite that i define is added to the main test suite. i will revisit this in "lessons learned" below
19 end of the class definition
20 this declares a variable of type pointer-to-YourTest and initializes it with a new instance.
21 end of the anonymous namespace.
lessons learned
- well it does work in text mode, doesn't it?
- unit testing means writing code: it is for programmers, after all
- the thing-tested here is a string-object. however, the stuff we have written only uses a string,
it does not explain what a string is or how it works. this means you do not need to mix your project
code with the test code in the same file.
- if you ever want to test a class you wrote, all you need for the automatic test is the header file.
- a typical test will create a thing-to-be-tested, then use assert-methods to do the real checking
- tests are organised in cases and suites somehow. we will need to learn what that really means and how it works.
but we can say a little at this point already. apparently, you can pack some tests together in a test
suite - like files in a folder. the smart thing here is that a folder full of testcases (suite) is again a testcase
that can be added to a suite. and as if by magic there is a master suite provided into which we can drop
ours. when the test is eventually run, our stuff just travels along in the same bus as everybody else.
(notice that we need not worry about the bus driving part...somebody has shown us much consideration here)
- running a test means compiling and running your test application.
- the test application does not need to contain a main function, this is given to you
from the library (through that unit++.h you include.)
- what you get for your trouble is free statistics and no manual checking of log files.
- not learned here but important anyway: never mix test code and project code in the same file anyway
not so bad for 20 lines, huh? this could become really useful if we learn to use it well.
open questions
- how do i best exploit the test hierarchy facility provided by test suite, test case etc in my project?
any more rabbits in the hat?
- how do i go about testing a method of an object that i have under test?
- so where's that gui then ?
- anything i need to keep in mind about naming conventions?
- (leaving space for you to add your own...
some of these get addressed in the next step, some don't.
2. step 2: let's get ambitious
a toy project
i want some simple application that will allow me to try and use the test organisation features.
i want to demonstrate the calling of methods in that application. i also want to demonstrate data access.
so lets try this:
- a car shall have a maker and a color.
- the color can be changed but there must always be one
- the maker can not be changed and there must always be one
- for simplicity's sake, i will restrict colors to RED, BLUE and GREEN
- for simplicity's sake, i will restrict makers to CRASH, BOOM and BANG
now imagine you would buy this piece of software from some software house. how would you know that
you get what you pay for? weeeelllllllll.............. ? So let's write the tests:
write a file cartest.cpp that contains these lines (not the numbers, no copy-paste, as above)
you may find this in a directory #0 somewhere near this file
1 #include <unit++.h>
2 #include "car.h" //see lessons above: include your header file
3 using namespace std;
4 using namespace unitpp;
5 namespace {
6 class CarTestSuite : public suite
7 {
8 void testCarHasMaker() {
9 Car myCar;
10 assert_eq("maker check", CRASH, myCar.getMaker() );
11 //this is a design decicion: default maker is CRASH motor co.
12 }
13 void testCarHasColor() {
14 Car myCar;
15 assert_eq("color check", BLUE, myCar.getColor() );
16 //another design decision: default color is blue
17 }
18 void testColorChange() {
19 Car myCar;
20 //we could do this
21 //assert_eq("colorchanged check", BLUE, myCar.getColor() );
22 myCar.setColor(GREEN);
23 assert_eq("colorchanged check", GREEN, myCar.getColor() );
24 }
25 public:
26 CarTestSuite() : suite("CarTestSuite")
27 {
28 add("CarHasMaker", testcase(this, "testCarHasMaker", &CarTestSuite::testCarHasMaker));
29 add("CarHasColor", testcase(this, "testCarHasColor", &CarTestSuite::testCarHasColor));
30 add("ColorChange", testcase(this, "testColorChange", &CarTestSuite::testColorChange));
31 suite::main().add("CarTestSuite", this);
32 }
33 };
34 CarTestSuite * theTest = new CarTestSuite();
34 }
prepare for a mild shock. are you sitting comfortably? good. then compile this.
WHAT??????????????
of course it won't work. we haven't written the Car.cpp yet, there is no Car.h. how could it work?
well this apparently is how "test first" works. you write the test first then check that they fail. this failure
is the formal reason for writing any application code at all. and then you go on and write as little
code as possible to make the tests work. if that code then doesn't make any sense, you know you need
better tests.
g++ -o cartest cartest.cpp -lunit++ -Wall <enter> complains he cannot find this file Car to
include from line 2. ok, give him an a car.h file. notice the compiler does not complain about the content
of this file, so let's construct a file car.h with no content at all. then we compile again.
Ah, that's much better now: a list of complaints like so:
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasMaker()':
cartest.cpp:9: `Car' undeclared (first use this function)
cartest.cpp:9: (Each undeclared identifier is reported only once
cartest.cpp:9: for each function it appears in.)
cartest.cpp:9: parse error before `;'
cartest.cpp:10: `CRASH' undeclared (first use this function)
cartest.cpp:10: `myCar' undeclared (first use this function)
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasColor()':
cartest.cpp:14: parse error before `;'
cartest.cpp:15: `BLUE' undeclared (first use this function)
cartest.cpp: In method `void {anonymous}::CarTestSuite::testColorChange()':
cartest.cpp:19: parse error before `;'
cartest.cpp:22: `GREEN' undeclared (first use this function)
line 9: so Car is undeclared? alright, lets declare it, in car.h, which now looks like this:
1 class Car {};
cycle - still as bad as before. errors from line 10 down, like so:
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasMaker()':
cartest.cpp:10: `CRASH' undeclared (first use this function)
cartest.cpp:10: (Each undeclared identifier is reported only once
cartest.cpp:10: for each function it appears in.)
cartest.cpp:10: no matching function for call to `Car::getMaker ()'
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasColor()':
cartest.cpp:15: `BLUE' undeclared (first use this function)
cartest.cpp:15: no matching function for call to `Car::getColor ()'
cartest.cpp: In method `void {anonymous}::CarTestSuite::testColorChange()':
cartest.cpp:22: `GREEN' undeclared (first use this function)
cartest.cpp:23: no matching function for call to `Car::getColor ()'
it is actually true on my system that the complaint re line 23 is new. strange...
now this obviously indicates that we didn't implement those methods - which we knew already, by the way - and now
we have a reason for implementing them: a production fails and it is all our fault!. can you see the managers
jumping up and down and eating their ties? while foam is dripping from their mouths?
however, because this is my text and the fancy just tickles me, i'll start with the BLUE, GREEN and CRASH.
not being overly malicious, i change car.h till it looks like this:
1 enum Color {
2 RED,
3 BLUE,
4 GREEN
5 };
6 enum Maker {
7 CRASH,
8 BOOM,
9 BANG
10 };
11 class Car {};
cycle and and all references to GREEN and BLUE have disappeared from the error list above, as well as that to CRASH. isn't
it cute how we didn't even need the car.cpp file up to this point? here's what we have now:
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasMaker()':
cartest.cpp:10: no matching function for call to `Car::getMaker ()'
cartest.cpp: In method `void {anonymous}::CarTestSuite::testCarHasColor()':
cartest.cpp:15: no matching function for call to `Car::getColor ()'
cartest.cpp: In method `void {anonymous}::CarTestSuite::testColorChange()':
cartest.cpp:22: no matching function for call to `Car::setColor (Color)'
cartest.cpp:23: no matching function for call to `Car::getColor ()'
now i shall write signatures for those car methods in the header file, i.o.w. add this to car.h, and notice as i
do so that line 11 needs a little change as well:
11 class Car {
12 Maker getMaker();
13 Color getColor();
14 void setColor(Color _color);
15 };
this earns us a new complaint: them methods are private. hard dame! well you know what to do, do
it after the old line 11:
11 class Car {
12 public:
13 Maker getMaker();
14 Color getColor();
15 void setColor(Color _color);
16 };
And here is what i get for my trouble.
/tmp/ccj0bKbB.o: In function `{anonymous}::CarTestSuite::testColorChange(void)':
/tmp/ccj0bKbB.o(.{anonymous}::CarTestSuite::gnu.linkonce.t.testColorChange(void) +0x15): undefined reference to `Car::setColor(Color)'
/tmp/ccj0bKbB.o(.{anonymous}::CarTestSuite::gnu.linkonce.t.testColorChange(void) +0x40): undefined reference to `Car::getColor(void)'
collect2: ld returned 1 exit status
So is this what you call progress? Actually, yes. You may notice that in this
mumble jumble there is no reference to the source file cartest.cpp any more. this is g++'s way of reminding me
that i didnt actually implement those methods i have prototyped. mother dog won't let me get
away with anything...
Remark: This might be the moment for a little experiment. stop working on this. take a walk.
come back in 45 to 90 minutes. with any luck, you will have forgotten all about this. i humbly
suggest that all you need to do to get back in (you do want back in, don't you?) is rerun the last step,
which was a compile. (once we have testable code, it will be the test execution instead, but for now
the old compiler is doing us plenty of favours, ain't that true.
alright then, time to stand up and be a cheat. we will never do more than absolutely necessary.
right now, them linker alarms tell us we need to implement them methods. doesn't say we have to
be very smart about it. make a car.cpp like so:
1 #include "car.h"
2 Maker getMaker() {
3 };
4 Color getColor() {
5 };
6 void setColor(Color _color){
7 };
ok, 3 methods that do precisely nothing. what do we get? hold on to your ...oops, same errors! what happened?
let's think a minute. we edited the car.cpp file. then we compiled the cartest.cpp file, which doesn't know
anything about car.cpp, since all it wants is car.h, which we left unchanged this time. where do we leave
news that cartest really does need car.cpp? i didn't know this myself so i checked in a book and the answer i found
is "in the makefile". what makefile? i stole one from another project and made some obvious changes to get this:
all: cartest
cartest: car.o cartest.o
g++ -o cartest car.o cartest.o -lunit++
car.o: car.cpp car.h
cartest.o: cartest.cpp car.h
in theory, this should mean the following:
a) to make all, build cartest.
b) to build cartest, do stuff with car.o and cartest.o
where doing stuff means to call g++ to link some .o files and mind them libs
c) to build car.o, play with car.cpp and car.h
and just know in deep & mysterious ways that play means compile up to the .o file generation
d) cartest.o depends on cartest.cpp
well,as i said, in theory. in practice this doesn't work and i am getting rather tired so i'll call it a day for now.
time passed....and i followed my own advice and had a beer. i think i have seen the light. i run make one more time
(to get back in, as explained above) and get this:
g++ -c -o car.o car.cpp
g++ -c -o cartest.o cartest.cpp
g++ -o cartest car.o cartest.o -Wall -lunit++
cartest.o: In function `{anonymous}::CarTestSuite::testColorChange(void)':
cartest.o(.{anonymous}::CarTestSuite::gnu.linkonce.t.testColorChange(void) +0x15): undefined reference to `Car::setColor(Color)'
cartest.o(.{anonymous}::CarTestSuite::gnu.linkonce.t.testColorChange(void) +0x40): undefined reference to `Car::getColor(void)'
collect2: ld returned 1 exit status
make: *** [cartest] Fehler 1
machine cannot find the reference to my functions because...oh dear, oh dear i haven't forgotten my syntax again have i?
i have. how do you tell a method that it belongs to
one class and not another? you prefix it with the class name and those ::. did i do this in car.cpp? no. who
is an idiot? i am. if this were available as recorded music, would you buy it?
so i fix this and compile and - horror of horrors - i get a full execution, even with my grandiose makefile
the moment of truth then: will it execute? type ./cartest and strike ENTER and...
(...drumrolls...)
FAIL: testCarHasMaker
maker check [expected: `0' got: `-1073745573']
FAIL: testCarHasColor
color check [expected: `1' got: `-1073745573']
FAIL: testColorChange
colorchanged check [expected: `2' got: `-1073745573']
Tests [Ok-Fail-Error]: [0-3-0]
YES! failed tests. great!!!
just what we wanted at this point, remember? to commemorate this event, i have put cartest.cpp,
car.h, car.cpp and makefile in their current state in a directory #1 somewhere around here
note: in a previous version of this text i did had coded a mistake in my assert_eq-calls, mixing up
parameters 2 and 3. the result was that in the test log the values for "expected" and "got" were
the wrong way round. i failed to pay attention to this, but fortunately Claus spotted it right away. i
checked his remark by looking at assert_eq.html and found confirmation (what else?!?). well here is one
example how the docu can help if you chose to read it. which i herewith recommend ;-)
so now we can go about fixing these bugs until the tests pass. take them in order. what's
the cheapest way to satisfy the getMaker check? let this method return CRASH. cheap? sure,
i said so, didn't i? run make then test. 1 ok 2 fail 0 error. good.
same thing for the getColor method gives 2ok 1 fail. we'r winning, friends....
well this is intersting now. we are supposed to set something that isn't even there. my
silly little car class has no data. change this in car.h.
break
do not add the "maker" piece of info there just yet. it belongs there for sure, you know it, i know it, but we havent
yet written a test for this thing, and this is just what unit testing is supposed to be good for. so,
just the color field. and nothing else. now cycle. the setColor test still fails. and that is great.
effectively this is what now forces me to beef up that cheap getColor routine.
ok, fix getColor now and cycle....actually, maybe not. i don't want this to get too silly after all.
too late, you say? my apologies... there is a number of things wrong here and if
even i can see that then it must be obvious. so what we do now is this:
1) introduce the color field in car.h (done)
2) adjust the get method to return this.
3) adjust the setColor method to write that field.
4) remember that we have not written any constructors for our car. this means it enters the world
without a maker and a color. will that show in the tests? let's code 1 through 3 and try what that does
before we write code to clear up this item 4. in other words there is now in car.h
* a new line 13 private:
a new line 14 Color color;
and in car.cpp we have
#include "car.h"
Maker Car::getMaker() {
return CRASH;
};
Color Car::getColor() {
return color;
};
void Car::setColor(Color _color){
color = _color;
};
time to cycle. i got this remarkable result:
FAIL: testCarHasColor
color check [expected: `1' got: `-947200']
Speicherzugriffsfehler
Speicherzugriffsfehler = memory access error for the not so teutonic
is this a surprise? well, the testCarHasColor method has failed. that's the second of my tests, which
worked a minute ago. i clearly destroyed something. namely the unconditional return of BLUE in that
get method. the answer of course is the missing constructor: the init-car has no color, iow some
rubbish is in that piece of memory allocated for color. and i was lucky that the rubbish didnt
accidentally match with one of my Color values defined in car.h.
remark : i am not really happy with this. the accidental element says i don't really have
a good test for "does the constructor do its job right?". this may be trivial in car.h, but imagine
that problem in a real world project, with 20 varieties of overload and whatnot...there ought to be
a technique for checking this sort of thing....can't think of one right now, though.
so i shall write a constructor and init my car as promised in that 4 line spec way above. i also
stop playing blind and introduce the maker field with a reasonable get method - nothing in
any unit test literature told me to not apply common sense when i see a chance. my code
bits now look line this (you may find hem in subdir #2 for reference):
car.h:
enum Color {
RED,
BLUE,
GREEN
};
enum Maker {
CRASH,
BOOM,
BANG
};
class Car {
private:
Maker maker;
Color color;
public:
Car();
Maker getMaker();
Color getColor();
void setColor(Color _color);
};
car.cpp:
#include "car.h"
Car::Car() {
maker = CRASH;
color = BLUE;
}
Maker Car::getMaker() {
return maker;
};
Color Car::getColor() {
return color;
};
void Car::setColor(Color _color){
color = _color;
};
i cycle, and i get
3 OK test cases, no fails, no errors, no memory access errors.
back to work, then. i have 3 tests and all pass. time to invent some new functionality.i haven't
really used my 3 car makers yet. i need a way to tell a new car who made it. write a
new test case.
void testAllColorsAndMakers() {
Car RedCrashCar(CRASH, RED);
Car BlueBoomCar (BOOM, BLUE);
Car GreenBangCar (BANG, GREEN);
assert_eq("initcheck", RedCrashCar.getColor(), RED);
assert_eq("initcheck", BlueBoomCar.getColor(), BLUE);
assert_eq("initcheck", GreenBangCar.getColor(), GREEN);
assert_eq("initcheck", RedCrashCar.getMaker(), CRASH);
assert_eq("initcheck", BlueBoomCar.getMaker(), BOOM);
assert_eq("initcheck", GreenBangCar.getMaker(), BANG);
}
and add it to the test suite. i cycle naively and find that i have introduced new notation: there is no constructor
with parameters, as the following complaints show:
g++ -c -o cartest.o cartest.cpp
cartest.cpp: In method `void {anonymous}::CarTestSuite::testAllColorsAndMakers()':
cartest.cpp:26: no matching function for call to `Car::Car (Maker, Color)'
car.h:16: candidates are: Car::Car()
car.h:20: Car::Car(const Car &)
cartest.cpp:27: no matching function for call to `Car::Car (Maker, Color)'
car.h:16: candidates are: Car::Car()
car.h:20: Car::Car(const Car &)
cartest.cpp:28: no matching function for call to `Car::Car (Maker, Color)'
car.h:16: candidates are: Car::Car()
car.h:20: Car::Car(const Car &)
make: *** [cartest.o] Fehler 1
ok, let's code one by adding the following to car.cpp:
Car::Car(Maker _maker, Color _color)
: maker(_maker), color(_color)
{ };
lets also add as new line 17
Car(Maker _maker, Color _color);
to car.h. now cycle. how disappointing, this works first time round. to be fair, i read up about
constructors with parameters before i wrote this.i left this in subdir #3 for your convenience.
goal checking
i wanted to do this:
i want some simple application that will allow me to try and use the test organisation features.
i want to demonstrate the calling of methods in that application. i also want to demonstrate data access.
i have used methods if only very simple get/set-stuff. by implication, i have also seen data access.
and i have seen how several test cases go together to make a test suite.
but i also have a few more things that i would like to see, like for example:
- using several test suites: why do i even need that ?
- i seem to get by using that assert_eq method. what else is there, and why?
and then, there is the fact that i thought i needed to test the first of my two constructors. i did change
my third test case to do this (even if only out of desperation...). by now, testColorChange does a lot
more than that. i should really refactor this before i do anything else. like move the call to the
default constructor to the fourth testcase.
while i am at it, the getColor and getMaker tests seem redundant now, because that happens in cases 3 and 4 as well.
however, all these things smell like some step 3, and i promised to get by with two.
is it time to stop this?
frankly, i think so. this thing has gone far enough to show a number of things:
- i can work with this even if i don't understand every itsy bitsy detail of it's inner workings
- some problems i come across look juts like lack of experience and concentration, like all those typos and stupid
handling errors. they have nothing to do with unit testing but make life difficult no matter what
i try to do.
- some problems i expected i simply didn't have: like namespaces, multiple test suites, using
other asserts, reading every file in the html doc and understand the content, etc etc etc. all those
continue to be goals for my studying, but not for this little text.
- some issues i stumble across are covered in literature that is dedicated to unit testing in general,
or perhaps to c++ programming, or software development processes. this text never wanted to highlight
everything the masters put to print by examples of my own.
so, let me wrap this up and leave you so you can try to fall on your own face ;-)
a few comments on the feel of all this
from wanting to learn c++, i have migrated to reading about light weight processes, in which unit
testing can play a significant part, and a few other things.
my current understanding is that "learning c++" is a
rather short sighted proposition in as far as any programming language isn't just a bunch of syntax rules
and flame wars about notation and so forth: they are means, not ends.
i find that this test first approach keeps me focused as i write. at my level, even a failed compile
is a help - i am sure this is not part of the big *unit tests to save the world* plan, but it seems to
blend in rather nicely. or maybe it compensates for the fact that i have no sidekick to do pair
programming with when i sit in my cavern and hack.
i don't write so much code at any given time - just enough for a little test or three,
but i write until stuff more or less works. for a little code change i only need to touch one or two files
at a time. this is much easier to control. also, i can go away and do something else most any time. it
helps me to look at my workload as a pile of handfuls (each of which is more or less easy) rather than a
mountain (which can be depressing sometimes).
i find unit++ nicely unobtrusive for this style of work. it just quietly sits there and waits to be used.
i look forward to carrying on in this manner as i continue to worm my way through my c++ books. more
questions will come up, some of those open items i listed above, some will lose their apparent significance.
i will learn...
oh, before i forget: thanks for keeping me company on my cerebral mud paths up to this point. writing your
own live commentry during he first few stunbling steps has been a strange experience but mostly fun. i do
hope this carried over and you don't feel you wasted your time entirely.
PS-1: a few points for those who want more.
there is any number of things that i can and will not discuss here because they are not first steps.
some of the more obvious points follow:
unit testing in general
there is great debate about when to use unit testing and what it can achieve. it makes sense to
follow this debate, but i won't replicate this here. search the web or read some books and articles.
when it gets hard in a real project
sadly, i have nothing to say on the matter since i am still very much in the learning phase. i know there is
literature out there discussing the ins and outs of unit testing in the face of concurrency, distributed
computing, and other such harsh realities. if you have these problems, please accept my congratulations for
having escaped the scope of this text.
unit testing in other programming languages
this again is a wide field. naturally, programming language specifics impact the things any
available implementation of XUnit can offer. one example is the so called refection mechanism
in java which is used in JUnit (according to what i read, anyway). this simply does not exist
in C++ so unit++ cannot use it, so here are two XUnit flavours with differing functionality.
this text is about getting started in one implementation. the rest is nice to know, quite possibly
important, but not my concern here. search the web if your mileage varies.
integration into your IDE
this is happening all the time anyway. unit testing is *hip* right now and tool makers are
responding, from what i gather. me, i want to learn the knitty gritty of my new language first then
start playing with new tools which is why i have stuck with the text only approach.
extending this framework
if you have a real project on your hands, you may find that you want different asserts. my understanding at
this point is that you would be expected to add such functions and make them available for the
restoftheworld if they work for you. don't let my ignorance stop you.
installation issues and using the gui
the installation - for me, anyway - was pretty much the usual:. /configure-make-make install. do use the --with-qt option
if you want the gui - and read the text bits in the configure file for this and other related info.
much to my shame, i could not make the gui work. maybe this is somehow related to the fact that i
am using qt3 libs (not qt2), but we simply don't know at this point.
i like a green bar as much as anybody (in fact i like a whole lot of bars, cheers) but since it just
wont work on my system, i am applying pragmatics and remain in text mode while learning. i wish you better luck...
i receive info from behind the curtains that this particular gui implementation may not be the final
word on the matter, so you may want to watch the sourceforge for updates or - heck, why not? - come and help!!
PS-2: things to read for understanding everything else
the following is an unordered list (and this is not a html joke)
German stuff
English stuff