Feed on
Posts
Comments

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.

Ten years ago I had a rendezvous with a beautiful girl, and at the end of the evening I gave her a ride home. Back than I thought playing the gentleman to be posh, so I opened the door for her. She slid with one elegant movement into her seat and I paced around the car and folded my wiry frame behind the steering wheel. I looked at her.

“Don’t you want to start?”, she asked and watched me curiously. “Um”, I said, obviously always finding the right words at the right moment. “Um. – Not before you buckle up…”. She frowned at me: “But I never buckle up”. I replied “Well, if you don’t buckle up, we’re not gonna go anywhere tonight”. “Oh come on!”, she now somewhat furiously stated. “Nope!” I insisted eloquently, finally feeling her shield of stubborn resistance falter. It took a few seconds before she realized that I really wouldn’t drive her home without her being properly protected from falling through the windshield and decomposing her pretty head by hitting the next best fireplug. So she buckled up.

Even back than I was so used to the secure feeling of the protective belt that just thinking of driving unbelted drove an uneasy quiver through my guts. This feeling is so strong that if I don’t drive strapped when moving the car just a few inches there’s always a sense of awareness that makes me want to fasten the seat belt immediately.

Today I felt exactly the same way while writing code.

The path of the test

At the beginning of the last iteration we identified a story that affected some legacy modules in our code base. When we recognized that the changes we needed to make would touch more code than we had thought, so we decided to try to test drive a part of the system from scratch to replace the tangled old code. So Richard, Reinhard and myself started to pair on the story alternately. Besides some private experiments with a Sudoku solver in Java this was the first time I was doing real full time TDD pair programming for a couple of days. Aside from some initial irritation and the constant realization that pair programming is hard to learn I was quickly pulled into the red-green-refactor cycle, as usual. But this time I held the pace for longer than ever. And was pulled into the cycle deeper and deeper. Write a test, make it work, look for redundancy. Write a test, make it work, look for redundancy. Write a test…

Today I wanted to quickly integrate the changed interface into an existing module. I didn’t have a test yet. The cpp file was readily opened in my editor. Just a quick edit, nothing more than integrating this interface. A few simple edits. Only three lines or something. And suddenly a nagging question materializing in my head:

How can I make sure this works?

At this moment I felt like driving unbuckled. I felt unsafe. I wanted my cozy safety net back. Like an addict I went for the next test.

What use is a seat belt when you hit a tree at 200 MPH?

Since I’m the one driving the adoption of XP in our company, I wanted to try TDD for myself on a save playground to learn more about the ins and outs before applying it at work. Since Java has really nice tools for TDD, I started test driving a small Sudoku solver in Java. This was my first real test driven code and I often wondered about how nicely the test suite covered my errors. Spirited in the Agile fashion, I began with a really straight forward brute force implementation. Everything went a lot more smoothly than I had expected and after some coding I had a simple solution that needed over 90 seconds for one simple Sudoku.

After a while I wanted to optimize the runtime. So I introduced some caching variables. I struggled with the failing tests as my solution grew more and more sophisticated, but the tests helped me to get to a deeper understanding of the real problem. Finally I arrived at a point where the algorithm managed to work through 1400 Sudokus in less than a second. I was thrilled. And I wanted more. So I installed a profiling framework to find out where the next optimization sweet spot would be hidden. When I browsed the profiling data I realized that the real solver didn’t even call the algorithm. So I had benchmarked a program that didn’t solve any Sudoku at all.

At this moment I felt like hitting a tree with 200 MPH, suddenly realizing that it is not a good idea to drive that fast into a 90-degree turn on a wet street, even if you have a seat belt.

After the blood had returned to my head on it’s way to my brain I implemented a test into the main program to check every solution with a simple algorithm before claiming to have solved anything. In the meantime I have a solution that runs 1400 Sudokus in 6 seconds on my core 2 notebook. I’m even quite convinced that I got the solution part correct…

Do I get my driver’s license?

So, here’s the lesson I learned on this journey on my path to the test:

  • Don’t rely on your tests too quickly.
    If you want to heed the XP advice to “test everything that can possibly break”, be aware that it’s often the things of which you think that they can’t break that finally break.
  • Use a healthy mixture of tests on all abstraction levels.
    Unit and functional tests are orthogonal – they cover different aspects of the code. But of course you’ll already have a lot of unit and functional tests if you don’t rely on your tests too quickly.
  • Buckle up!
    The unsafe feeling while trying to modify code without having a test was a very impressive experience for me. I know that from now on I’ll fasten my code’s seat belt.

Scene 1. Karl and J.B. are pairing on a small web service. Karl is just returning to the workplace with a steaming, hot cup of coffee in his hand.

Karl: ‘Let me see what you wrote just now…

usUserName = request.getParameter("UserName");

Um… This variable, usUserName, what does the us-prefix stand for?’
J.B.: ‘Well, that is the unescaped user name the way we get it from the user. I wanted to make sure that we don’t accidentally write it into a database or send it back in it’s evil, unescaped form to the webbrowser. If we use the us-prefix every time we have an unsafe string, we’ll immediately recognize any error that could otherwise escape us because we will learn to look for such errors. This is the application Hungarian notation I read about over at Joel’s site, where you actually use a prefix that has a meaning instead of just a shorthand for the type.’
Karl: ‘So… why not just call it unescapedUserName?’

Confusion

In a time where you enter veryLongVariableNames by typing ‘v’, ‘e’, ‘r’, Crtl-Space, I don’t see why we can’t finally get rid of TLAs. You know that the Hungarian notation got out of hand when your colleagues check in code that changes ucpBuffer to pucBuffer (”fixed a segfault”). Why not just name a variable for what it contains, in plain old English? I definitely know that I should think about my method name if my partner asks during a pairing session: “And what exactly do you intend to do in this method?”.

In which example is the error easier to spot? Does the second example really take longer to write? To read? To understand?

for(unsigned int i = 0; i < iLineCount(); ++i)
{
  for(unsigned int j = 0; j < iNodeCount(i); ++i)
  {
    pGetNode(i, j)->layout();
  }
}
for(unsigned int lineIndex = 0;lineIndex < getLineCount(); ++lineIndex)
{
  for(unsigned int nodeIndex = 0; nodeIndex < getNodeCount(lineIndex); ++lineIndex)
  {
    getNode(lineIndex, nodeIndex)->layout();
  }
}

If you can really remember mnemonic prefix TLAs (or any TLAs for that matter) and think Hungarian notation or abbrVarNames are a great way to safe yourself some typing, please let me know.

CMake is one of the best build tools out there. It has a nice command line interface and comes with an even nicer GUI. Unfortunately the GUI is MFC based, which means you need a VC professional license to build it for windows and you can’t use it in linux.

Since Trolltech released it’s wonderful GUI framework Qt for windows open source development some time ago, I decided to combine my eagerness to learn TDDing GUI apps with my need for a nice cmake GUI – and to start developing qcmake.

The first priority for me was to learn how to TDD a GUI application. CMakeSetup, the MFC application qcmake should be able to replace, has a very simple single-window interface, so this should be the ideal playground to get an idea of the basic GUI testing problems.

Setting up the testing framework.

The first step to successful TDD is to set up a test environment where you can execute your tests with a single keystroke from within your development environment. I spent some time integrating Qt’s testing framework qtestlib into ctest. Hitting F5 from my Visual Studio Express executes all the tests. If something goes wrong, the qtestlib framework prints the debug output into the Visual Studio output window. This way I can just click on the error message to find the offending code, or just enable a breakpoint step through my personal mess…

Top-Down or Bottom-Up – the duck’s decision

The testing framework is ready and eagerly waiting for it’s first real test. But somehow I don’t know where to start. The options are quite simple: either the good ol’ bottom-up approach, implementing one layer upon each other until I reach the top, or the top-down development 2.0 methodology where everything is faked or mocked, slicing the whole vegetable vertically until the feature is finished.

Since the top-down approach resembles the design-driven process the most (plus the running tests, minus some heavy documents) and Heusser & McMillan’s presentation Interaction Based Testing at GTAC made my mouth water (I really like chocolate flakes), I thought I’d go for the top-down method.

My first user interface test

And finally my first test looks like this:

#include "QCMakeTest.h"
#include "QCMakeWidget.h"

#include <QtTest/QTestMouseEvent>

void QCMakeTest::shouldEmitConfigureSignalOnConfigurePressed()
{
  QCMakeUi::QCMakeWidget* qCMake = new QCMakeUi::QCMakeWidget();
  QSignalSpy configurePressed(qCMake, SIGNAL(configure()));
  QTest::mousePress(qCMake->getConfigureButton(), Qt::LeftButton);
  QCOMPARE(configurePressed.count(), 1);
}

QTEST_MAIN(QCMakeTest)
#include "QCMakeTest.moc"

That was a lot of work just to get started with a simple test and basically no functionality. Fortunately I have some TDD experience to build upon, and right now this experience tells me that the up-front effort will pay of in the short run due to not debugging a lot. Up-front effort, quicker development, isn’t that what BDUF was all about? I’m curious where all this will lead me to…

Software is complicated. More often than not it’s a complicated mess. Sometimes even a tangled complicated mess. And wherever you look all you see is tradeoffs. There are no easy solutions ™. The dll hell is replaced by the side-by-side hell. Emacs is better than Vim, Vim is better than the Visual Studio editor and the Visual Studio editor is better than Emacs. The Visual Studio editor even has a kill-ring (Ctrl-Shift-Ins). But Emacs has a web-browser. Vim is way cooler because I can’t remember the commands, even though they’re orthogonal. To what? Why not write a new editor in Erlang. Well, no, not me, I just want an editor that has all the features of Emacs, Vim and Visual Studio. Now. But without the bloat of Emacs. Or Visual Studio. More slick, just like Vi.

Since the early days of computer science, when software developers still had to wear suits at work and wrote A.I.s in Cobol, um, COBOL, with their feet, people tried to find out how software development could be made less complicated. And they soon discovered that the secret sauce is abstraction.

Abstraction

Layers. Components. Modules. Interfaces. Design. Architecture. It’s so easy: define an architecture, think of layers, interfaces, modules. Create a nice design that meets this architecture’s goals. Hire a bunch of developers to implement the components.

From this level of abstraction it really sounds easy. This is why it’s called abstraction: it hides the complicated details. The good thing is that as long as you work on this level of abstraction, it’s cheap to change your concept. Or as Joel Spolsky says:

Designing a feature by writing a thoughtful spec takes about 1/10th as much time as writing the code for that feature—or less.

Well, than it’s obviously a very good idea to do all the design first. After all, if you change your design, you’ll have to change your implementation. As long as you didn’t start writing code, changing your design is easy. Or even better, start at the architecture level. Hire the best consultants to create the perfect architecture. Hire some really bright guys to do your design. In the end, a bunch of monkeys can do the implementation. The dream of the pointy-haired boss came true!

“Um. Sounds easy. So, how do we know that our design is good?”
“This is easy: experience.”
“But to get experience I’d have to actually try the design, won’t I?”
“Yes, of course.”

“So, if my design is not perfect in the first place, I’ll learn this only when I try to implement it?”

“Well, yes, come to the point.”

“Then how can I finish my design before the implementation phase?”

“Um. Well. You just do iterations. Big iterations, I guess, because design is so much easier to change.”

“So I work for months on a design of which I don’t even know that I will be able to implement it?”

“Perhaps… easier to. Um, change…”
“And when I finally find out that my design was crap, I’m in the implementation phase, a deadline looming on the horizon and no time to change the design and all the code that was already written?”
“… – well, is there a different way?”

Feedback

Tradeoffs again. Working with abstractions means to get less feedback. “I’ll take the chair and hit the sentinel” will be a hard job if the chair turns out to weight a hundred pounds.

And feedback is important. One of the laws of software development is:
The longer it takes until you find out that you made an error, the more costly it is to fix that error.

This means that you should try to find your errors as quickly as possible. But when you’re working on a high abstraction level, you just don’t know all the complicated details because, well, that’s why you’re working on that high abstraction level, isn’t it? So you’ll find out that your design is crap when you’re in the “implementation phase”, at which point nobody has time to change the design. So you just live with the crappy design and run around cursing the designer and hating your job.

Fail!

One solution to discover your errors early, is to do Ultra Extreme Elite Programming (Joel Spolsky). Design just enough up front that you get an idea of where you’re going, write the target down as a test and sit down with a colleague to find a redundancy-free implementation. When you find out that your initial design is crap, which you’ll do very quickly, rely on your tests to help you refactor your code to a better design. Of course, as Joel puts it so beautifully, this is like driving around with the handbrakes on.

The question is whether driving around with the handbrakes on is really slower than driving at full speed with closed eyes and a plan. I think it mostly depends on where you want to end.

Defects On Sale!

Today after our planning game I did a short poll on how the guys perceive test driven development and pair programming. We’re trying to do both for some time now, and since I take the blame for introducing both practices, I feel I’m somewhat – um – preoccupied on that matter. A few days ago, I was caught totally off guard when Richard told me that, well, he doesn’t believe programming in pairs is more productive. Bummer. And I had believed my show to be grand circus.
Continue Reading »

Steve McConnell responds to Eric Wise’s article Rejecting Software Engineering that Rumors of Software Engineering’s Death are Greatly Exaggerated”. There’s a lot of fuzz about the usage of the word “engineering” when it comes to software development. What’s this all about?

What is engineering?

According to wikipedia, engineering is defined by the ECPD as

The creative application of scientific principles to design or develop structures, machines, apparatus, or manufacturing processes, or works utilizing them singly or in combination; or to construct or operate the same with full cognizance of their design; or to forecast their behavior under specific operating conditions; all as respects an intended function, economics of operation and safety to life and property.

When I look at this definition I don’t really see anything that would not be applicable to software development. Steve McConnell generously points out that software engineering is recognized and practiced for some time now, so where does this new-fashioned stubborn refusal to call the child by it’s name come from?

The engineering process

The real problem comes to light when you look at the engineering process. You’ll find a description of the engineering process on wikipedia. The article describes the engineering process in four stages:

  • Conceive
  • Design
  • Realize
  • Service

Now there are some engineers who map the engineering stages to software development in a rather funny way: They think that “Design” is the process of drawing good looking UML diagrams and that “Realize” is “Coding”. When you look at the wikipedia article, you’ll see that for engineers “Realize” stands for “Manufacturing”. In a software context, manufacturing means running the compiler and pressing some CDs or deploying some binary over the Internet.

So when engineers claim that software developers should look at how engineers do their design and all this talk about software processes would be settled once and for all, they’re ignoring that software development is a design-only activity and that software has a lot less problems with the “Realization” stage than traditional engineering.

When a software developer writes code she is building an executable, mathematical model of reality.

Conclusion

When software developers prefer not to use the title “engineer” to describe what they’re doing, they’re trying to avoid a mapping of the engineering process onto the software development process that is wrong. In the end, software developers will build mathematical models (source code) and apply scientific methods (complexity analysis) to solve problems. If this is not engineering, than we’re not engineers.

Are you a developer who dreams about a better software development process in the organization you work for? Maybe you read something about fancy practices on your favorite blog or mayhap you even touched one of those old-style paper collections called books? Do you have some concrete ideas on how to improve, but don’t know how to start? I was in the same situation a year ago. Here’s what I did and what I would do differently today.

Continue Reading »

When I arrived at work today I fired up Outlook and checked my mail. I found five mails from our auto-build server telling me that the build broke. Since we introduced test driven development and continuous integration only a short time ago this was not out of nowhere – the build usually breaks at least once a day.

But today was the first day a unit test broke since we introduced TDD and CI.
Continue Reading »

A month ago I bought a TREKSTOR NDAS device. This devices promises on it’s package to be linux compatible. So after I unpacked the hardware and everything was running in Windows I tried to install it in linux. Unfortunately the stock feisty debian package I found didn’t work with my WLAN configuration.

Now after I reinstalled ubuntu and upgraded to gutsy which comes right now with a 2.6.22er kernel I tried to build the driver from source. I had to patch the sources to make it work, but since it works flawlessly right now I provide my patch and a little compilation howto.

Download the current NDAS sources and my NDAS patch for linux kernel 2.6.22.

# installed some packages. I don't know which exactly, but you'll need
# at least the following:
apt-get install build-essential checkinstall linux-headers-generic

# extract and patch the ndas sources...
tar xvzf /path/to/ndas-1.1-2.tar.gz
cd ndas-1.1-2
patch -p1 < /path/to/ndas-1.1-2_kernel-2.22.patch

# you only need to set NDAS_KERNEL_VERSION if you
# don't want to compile ndas for the currently running
# kernel for example, if you're compling from within colinux
NDAS_KERNEL_VERSION=2.6.22-6-generic
make

# ndas_root must be exported for make install and
# checkinstall to work
export ndas_root=$(pwd)
# somehow I had to make install before checkinstall...
# this is no problem, since checkinstall will clean up
# the whole mess again
sudo make install
sudo checkinstall

After that you can start the NDAS service by issuing

/etc/init.d/ndas start

Configure your device by following the Ximeta NDAS driver documentation.

« Newer Posts - Older Posts »