Sunday, November 25, 2007

XPDays Germany 2007 - Ideas Going Mainstream

I had the opportunity to take part in the XPDays Germany last week. The company I work for enabled Uwe, our project lead, Holger and me to participate. It all started with a three and a half hour ride from Munich to Karlsruhe where we heroically overcame a nearly empty tank, a shaking car that felt like it just drank the wrong kind of gas and my own card reading skills - or lack thereof.

Day 1


In the end we arrived on time for a Randori session by Dave Nicolette and Rod Coffin. From the moment I learned of this experimental learning session where two people sit in front of a computer and test drive a piece of code while the whole audience is throwing in questions, I was kind of scared of the prospect of being watched while writing code by a hundred people - which is probably kind of normal, given that some people even dislike being watched while pair programming.

The session turned out to be very interesting. One of the key elements of this style of learning is that the audience's energy level stays high for a long time - you have to pay close attention, since due to the random selection of the next person to come to the front you could always be this person. And since you don't want to look like a fool when doing stuff in front of a hundred people (um, what was the problem, again?) my adrenaline level alone was enough to keep me awake.

The other thing I learned from this experience besides a new way to coach technical stuff is that there is a very good reason we do "pair programming" and not "group programming". Throwing two brains at a problem can be a very mighty tool to solve programming tasks, but in some situations throwing a hundred brains onto a very simple programming example felt like sitting in one of Dilbert's most unproductive meetings:

"So we add the game to the character: character.addGame(game)"
"But why don't you add the character to the game?"
"I see duplication, I see duplication!"
"But isn't this, um, less expressive?"
"Duplication is bad!"
"Why do you assign null to a variable, this is done automatically"

After some time of silent observation I realized that sometimes I am exactly like this! So to all of you who have to cope with me on a daily basis: just hit me on the head with a big club from time to time.

The next session was a series of "lightning talks" about all kind of Agile topics where I learned about Alistair Cockburn's Crystal, which is a set of methodologies built upon the Agile principles and an extreme tailoring approach.

Day 2


Despite the enormous amount of a whole liter of "badisches Helles" I had in the evening I was wide awake and ready to suck in new ideas during the conference's main part. The day started with an interesting presentation by Dave Nicolette about how to communicate TDD and design debt to your management. I was particularly stunned by the fact that he did talk about the cost of design debt and the refactoring part in the TDD cycle for a very long time without mentioning the cost of fixing an error in terms of when it is found and the shortened feedback cycles that TDD provides.

The next presentation was titled "why Agile projects fail", but turned out to be about why projects fail in general and provided some insight into the ideas of root cause analysis (5 Why), the dimensions in which failure can occur (The Broken Triangle), and the psychological factors that are the real cause of ineffective development practices.

After lunch the keynote by "Dark Side" Rod Austin from HBS showed how Agile development fits the icy wind of change that swirls today's leading companies in the world from a cost competitive to an innovative business model. After all, who doesn't want a designer trash bin?

Stfan Roock's talk on "Simplicity in Software Projects" was a very entertaining lecture on how easy it is to get so accustomed to complexity that you don't even realize how simple things could be. Well, that and that the Borg are the only entities in the universe who understand that when you travel through space aerodynamics is pointless.

In the end it was very interesting to see big German companies like SAP, EADS and Siemens to take interest in extreme programming. Looks like those ideas are finally going mainstream.

Friday, November 16, 2007

Does "Test After" Work For You?

"Why not write a test for this?"
"Why should I, it works..."

The idea of Test-After Development is to write a set of automated white-box tests after writing your production code. Since probably every CS student in the world has learned that unit tests are a good idea, you'd expect unit testing to be an industry state standard for quite a while now. Interestingly the idea of automated unit and integration test is lately becoming more popular due to the widespread use of Test-Driven Development.

So why do we need Test-Driven Development to be able to efficiently write automated unit tests?

  • If you write your code first and don't think about how to test the code, the code will not be testable. Thus testing becomes expensive and frustrating. Test-Driven Development will guide your software design by the old mantra of "how-do-I-want-to-use-this-class", leading to a highly decoupled design.

  • When you write your tests, you'll discover a lot of errors. But instead of the red bar in Test-Driven Development, which you expect, the red bar in Test-After Development is the demotivating sword of reality.

  • The most important reason why I have never seen Test-After Development work, is that developers just don't believe in errors once they wrote the code. This seems to be an eternal wisdom of software development psychology: once the code works, why bother testing it? Let's just implement the next feature.

Thursday, November 1, 2007

A Program Is Born: QCMake's First Functional Test

When you write a GUI that is just a thin layer for an existing business layer and you don't see how to integrate test fixtures into this business layer, you'll be down in the dirty functional testing work very quickly. This happened to me today when I tried to write my first test for a small Qt facade object for cmake.

I started the test very enthusiastically: To test cmake I create a directory, cd into that directory, create a CMakeLists.txt and let cmake create a CMakeCache.txt. In the end I know that cmake ran when CMakeCache.txt exists.

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}


I hit F5 and everything runs just fine. Once. The second time the directory ExecuteInCurrentDirectory already exists. Of course to have a nice and clean starting point the test must remove the test directory if it already exists. So I added:

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}

Green. Perfect. Now let's create a CMakeLists.txt.

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(QDir::setCurrent(testDirectory.path()));

QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));
}

Green again. Once. The test fails the second time it's executed:

********* Start testing of QCMakeControlTest *********
Config: Using QTest library 4.3.2, Qt 4.3.2
PASS : QCMakeControlTest::initTestCase()
FAIL! : QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
'currentDirectory.rmdir(testDirectory.dirName())' returned FALSE. ()
..\..\..\..\..\Source\CMake\Source\QTDialog\qcmaketest\QCMakeControlTest.cpp(30) :
failure location
PASS : QCMakeControlTest::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped

Yep, no problem, all I need to do is to rmdir recursively. Just a quick glance into the Qt docs. But I found nothing. Well, it's not too hard to implement a recursive rm -rf, but still... I was so sure that this function would be hidden somewhere that I spent more time googling and doc-reading than implementing it when I finally realized that I was on my own. So in the end the test looked a little bloated:

#include "qcmaketest/QCMakeControlTest.h"

#include "qcmakeui/QCMakeControl.h"

bool removeRecursiveForced(QDir& directory, const QFileInfo& entry)
{
if(!entry.isDir())
{
return directory.remove(entry.fileName());
}
QDir directoryEntry(entry.filePath());
QList entries(directoryEntry.entryInfoList
(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
for(int entryIndex = 0; entryIndex < entries.count(); ++entryIndex)
{
if(!removeRecursiveForced(directoryEntry, entries.at(entryIndex)))
{
return false;
}
}
return directory.rmdir(entry.fileName());
}

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(removeRecursiveForced(currentDirectory,
QFileInfo(currentDirectory, testDirectory.dirName())));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));

QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));

QCMakeControl qCMakeControl;
qCMakeControl.configure();

QFile cmakeCache("CMakeCache.txt");
QVERIFY(cmakeCache.exists());
}

#include "QCMakeControlTest.moc"

At least I have an idea where this could lead me - a nice class to generate a clean cmake directory. But let's see whether I'll be right, perhaps YAGNI will finally get back at me. And if you know an easier way to delete a directory recursively with Qt, please leave a comment.