Best Practices in Delphi: Unit Testing and Continuous Integration
Unit testing, code coverage, continuous integration, are terms that despite having existed forever in environment such as Java or recently .NET, are somewhat relatively new to Delphi. Many of us, as developers have become somewhat ill-accustomed to the RAD environment that Delphi brings to the table and somehow throwing fundamental practices such as automated testing out of the window. In this article we’re going to go back to basics and learn about the benefits of automated testing and how we can achieve good practices by adopting a series of simple yet effective techniques, both from a personal and technical point of view.
Change …
One of the fundamental problems with software is change. Change can cause problems because change introduces just that: change. When a customer demands a new feature or a bug is fixed, we often end up with new bugs being introduced. Many of these go unnoticed because we don’t have a good and methodical test process in place. Throwing the finished product at QA, which in many cases ends up being the end customers, isn’t the best way for us to assure that a change doesn’t introduce side-effects.
One of the fundamental problems with software is change
To this purpose, the best way to prevent unintended bugs is to test the software thoroughly, and in doing so, we need to make sure we perform a certain number of tests that cover that greatest possible amount of scenarios our application can face. Since most applications, or at least those that concern us in this article, have a user interface, we tend to delegate the testing of our application by interaction with the user interface. That is valid, but only to the point where we actually want to test the correct functioning of the interface, and not other aspects such as the underlying business logic of our application. Unfortunately, tools that allow a somewhat automated level of testing of GUI application promote this kind of practice. We therefore end up with automated scripts that validate not only the correct functioning of our interface, but that of our business logic through a series of inputs and outputs that need to be present before and after a test is run.
The problem with these kinds of tests, apart from being extremely tedious to setup, is that it’s not entirely valid in all cases. Many tools rely on external factors to our system under test such as the position of an edit control or the order in which a series of events can be called. These might not actually influence the outcome of the unit of logic that we want to test, yet a difference between two runs in any of these factors can cause our test to fail. The root of the problem isn’t the tool, but the actual design of our application. There is no clear separation of concerns between the different layers of our system. We tend to mix user interface code with business logic, which in turn has data access code intermixed. All this leads to non-sustainable systems that cause problems and unintended side-effects when changes are introduced.
Layers and Testing
The typical desktop application has a GUI front end, connected to a backend database server, or middle-tier. Being used to Delphi, many, including myself, have been spoilt by the RAD environment Delphi provides (when I speak about Delphi, it also applies to other environments such as Visual Studio). It’s very easy to drag and drop controls on to a form, click the event handlers and start writing code. And to a point there is nothing wrong with that. The problem arises when we start writing the wrong code in the wrong place. When we start adding business logic or data access code to event handlers things start to get messy. On top of that, if we make incorrect use of data binding, it’s a recipe for disaster.
The main consequence is that we tie our business logic to our user interface, thus making it impossible to test without some sort of direct interaction with the interface, be it a manual test or an automated scripting test.
If on the other hand we were to separate the different parts of our application so that each part is concerned exclusively with the functionality it encompasses (separation of concerns), we could end up with a layout as shown below.
Here we are separating our user interface from our business layer. However, we’ve added an additional layer of separating the business layer from the data layer. This provides us with more flexibility by allowing us to change data layers based on our requirements. One day we might need to read and write from a database and another day want to use native files. By clearly isolating the data layer, we can implement this change without requiring a change in the rest of the application.
Unit tests
This separation brings us one more advantage; it allows us to test certain aspects of our application in isolation. Unit tests are about testing certain aspects of code in isolation from other parts. It is like black-box testing where the system under test (SUT) should be tested without interference of external factors. We should be able to create individual unit tests that verify our applications functionality. Additionally, if we automate these tests, we can validate that any new change or bug fix that is introduced in our application doesn’t break any existing functionality.
Unit tests are about testing certain aspects of code in isolation from other parts
There are numerous frameworks that allow us to run unit tests; however one of the most established ones for Delphi Win32 is DUnit which comes from Java’s JUnit. In the case of .NET, we have other alternatives such as NUnit, mbUnit or xUnit .Tests are created using attributes (in the case of .NET) or by descending from certain classes (DUnit) as shown in figure 1.

Fig. 1
We have two procedures that we want to test for a calculator class we’ve implemented: TestAdd and TestDelete. The implementation for the first is shown in figure 2.

Fig. 2
As can be seen, we setup some input values, call Calculator.Add which in our case is the SUT and validate the results. Check is similar to an assert in that it raises an exception if the condition is not met. To run this test we can use either the GUI or console version of the DUnit framework. Figure 3 shows us the GUI version where the tree structure contains a hierarchy of tests.

Fig. 3: Unit testing User Interface
Continuous Integration
Systems don’t work in isolation, as much as we’d like to think that testing individual units independently one of another would guarantee that our system will work on deployment, it is not the case. All these individual units need to be integrated so that we can make sure that they work together in harmony. One of the problems that occur is that we tend to leave this integration to the last possible moment and find that although on paper everything should work, once two systems interact, things act differently, being the common effect of that left by a nuclear explosion.
That’s where techniques such as continuous integration come into play. The either behind this is that we are constantly integrating different parts of our system in the order of magnitude of once an hour, more than once a day. Integrating the system also leads to the validation of integration tests, that is, tests that use the multiple objects that come into play to make sure they work correctly once combined.
Although continuous integration is more a mindset than a toolset, the latter is required by the former to make the actual process productive and efficient. There are numerous tools on the market, both free and commercial that allow for continuous integration scenarios. Finalbuilder Server, Team City or CruiseControl.NET are some of the well known ones. CruiseControl.NET is by far the most extended and is a port of Java’s CruiseControl, developed by Thoughtworks. It’s name is somewhat misleading since it can be used both for Win32 and .NET projects. Normally these tools are combined with build tools such as MSBuild, Nant or Finalbuilder in order to automate the process of building the software and running the appropriate tests.
More to come
If you want to see how we can use all these different techniques and technologies to create sustainable software, make sure you come to the Continuous Integration session I’ll be holding at the Software Developer Event on June 23rd in Ede, Holland.