Unit testing in KMyMoney

Just about the time, when the KMyMoney project underwent a radical change of it's inner business logic (the KMyMoney engine), I read an article about the existance of a unit test container for C++ projects named CPPUNIT. In discussions with my colleagues at work, I got the impression, that this would be something worth to look into. So I sat down and wrote the first test cases for existing code to get a feeling for what is required.

I found it annoying to write test cases for code that existed and was believed to work (version 0.4 of the project). When the decission was made to start with the 0.5 development branch, I started working on the new engine code that should introduce a clear interface between the business logic and the user interface. Another design goal was to write the engine in such a way, that it is not based on any KDE code which the old one was. The second goal to free it from Qt based code was not that easy and was skipped by the project team at that time.

Even if it was hard for me at first to follow the above laid out principle to design the interface, write the test code and then start with the implementation, I followed this trail. It has proven to be very valuable. Once the interface was designed, I started thinking in a different manner: How can I get this class to fail? What strange things could I do to the code from the outside? Those were the design drivers for the test code. And in fact, this thinking changed the way I actually implemented the code, as I knew there was something that would check all these things over and over again automatically.

A lot of code was implemented and when I was almost done with the first shot of the implementation, discussion came up on the developers mailing list about a feature called double entry accounting that was requested for KMyMoney by a few people. The engine I wrote up to that point in time did not cover the aspects of double entry accounting at all, though a few things matched. After some time of discussions, we became a better understanding of the matter and I changed the code to cover double entry accounting. Some of the classes remained as they were, others had to be adopted and yet others rewritten entirely. The testcode had to be changed as well due to the change in the interfaces, but not the logic of the tests. Most of the thoughts how to uncover flaws remained.

And that is another reason, why unit testing is so useful: You can change your internal implementation and still get a feeling, if your code is working or not. And believe me: even if some changes are small, one usually oversees a little side-effect here and there. If one has good unit tests this is not a problem anymore, as those side-effects will be uncovered and tested.

During the course of implementing the engine, I wrote more than 100 testcases. Each testcase sets up a testenvironment for the class and tests various parameters against the class' methods in this environment in so called test steps. Exceptions are also tested to be thrown. The testcases handle unexpected exceptions as well as expected exceptions that do not occur.