Using Delphi "Unit Aliases" to aid in unit testing
- Resolving unit dependencies. Think of a case where a unit uses another unit just so it can access one symbol from that unit. Assume that the used unit iteself uses many other units. In order to build a test for the code in this unit, the compiler must resolve all used units. Due the nature of the IDE source code, it’s very easy to get into a situation where a unit test ends up pulling in a very large number of source units that aren’t really needed for the test.
- Resolving IDE services accessed by the unit being tested. Think of a case where a unit being tested uses a core IDE service to, say, display status in the IDE splash screen during startup, or to output a message to the IDE’s message view (just two of many possible examples). Because the IDE isn’t running, it’s unlikely that these services would be available to the unit test.
There have been different approaches to overcoming these challenges, that those of us on the IDE team have taken when writing unit tests:
- Refactoring the unit being tested has helped in some of these cases.
- Another approach has been to litter the code being tested with {$IFDEF} or {$IFNDEF} directives so that only the code being tested is visible to the test. Yes, I use the term "litter" because of its negative connotation – using IFDEFS can compromise code readability and maintainability. Also, any time you change code (even if to only add/remove IFDEFS), you risk breaking something (yes, this may be less of an issue, since you are, after all, changing code so you can write unit tests).
- A third approach, which is the subject of this post, is to provide a unit with stubs for the dependencies and then use the Delphi compiler’s Unit Alias feature when building the unit test. A major benefit of this is that it allows you to test existing code without having to modify that code.
Take the over-simplified unit shown in this screenshot:
Let’s assume we want to write a unit test for TMyClass.DoSomething and that the OutputAMessage procedure 1) lives in a unit (MessageServices.pas) that pulls in a lot of extra units, and 2) requires UI that is not available while unit testing. A simple way to write this test is to provide a unit which provides an implementation of "OutputAMessage". Let’s call the unit TestStub:
Yes, you could use an IFDEF in Unit1’s uses clause to conditionally use either the MessageServices or TestStub unit. But what if the unit being tested is more complex? What if there are many functions/symbols/etc. living in spearate units that you want to provide a stub for? What if the code uses a unit name in order to qualify a symbol (i.e. what if the code in Unit1.pas referenced "MessageServices.OutputAMessage" instead of just "OutputAMessage" in order to resolve a symbol ambiguity)?
Unit aliases allow you to address those concerns. When compiling the unit test, if you define a Unit Alias of "MessageServices=TestStub", it will use the stubbed version in TestStub.pas. When you build your main project, it will continue to use the real version in MessageServices.pas. If you wanted to provide a single stub unit for multiple "real" units, you would just provide addtional aliases: "MessageServices=TestStub;AnotherUnit=TestStub". And due to the way Unit Aliases are implemented by the compiler, it will properly alias unit names used as scope qualifiers in the code. As mentioned earlier, a great benefit of this approach is that you can test your existing units without having to modify them at all.
You can define Unit Aliases in the IDE using the Project | Options dialog. In Delphi 2010, this option is on the "Delphi Compiler" page. When building from the command line, the appropriate compiler switch is -A (i.e. -AMessageServices=TestStub).
While the compiler’s support for Unit Aliases was originally added to increase backwards compatibility and to ease migration when a new version of a unit has a different name than an older version, I have found the feature to be quite useful in helping to unit-test code that might otherwise not be easily tested.
Share This | Email this page to a friend
Posted by Chris Hesik on November 24th, 2009 under Delphi, IDE, RAD Studio |



RSS Feed

November 24th, 2009 at 10:43 am
Thanks Chris, thats a nice idea.
November 24th, 2009 at 11:05 am
I’d love to see a collection of useful tips like these in a printed book.
Perhaps a "Delphi 2010 Cookbook" or a "Delphi 2010 Programming Language". Is such a book available in printable PDF or actual book? If someone curious about Delphi (the language) asks, what book can I recommend? If I suggest Essential Pascal, then any comparison to C#, etc. won’t even be close.
Anyways, thanks for the nice tip. I hope to see more.
November 24th, 2009 at 5:47 pm
Ignoring code interactions during testing seems short sighted at best.
November 24th, 2009 at 7:28 pm
It kinda feels like mocking at the unit level… definitely useful for legacy code. Although if you are writing new code and find yourself using this method to achieve unit testing then probably the code under test isn’t declaring it’s dependencies correctly (or the dependencies haven’t been abstracted to the level that they can be mocked).
@Xepol #3: This isn’t ignoring code interactions, this is unit testing. It’s a way of stating TMyClass does what it is supposed to do. Integration/End to End/Large testing verifies that TMyClass interacts with OutputAMessage correctly.
November 25th, 2009 at 7:25 am
Justin,
Yes, this technique is most applicable to pre-existing code, which is exactly the type of code I’ve used it on. It allows code to be unit tested without major refactoring (the refactoring can be done after the unit tests are in place)
Xepol,
As Justin points out, other types of testing will test the interactions. The stubs could test that the correct data is sent to the stubbed functions. Integration or System tests would later verify that the data shows up correctly in the UI. That is why unit tests are just one piece of the overall testing puzzle.
November 30th, 2009 at 7:42 am
If your function is so self contained that it does not call another function to produce its result, then perhaps you have a point.
On the other hand, if you are heavy into library and code reuse, then you have not really tested the function correctly unless it interacts with all its dependant functions. It can do 99% of its task correctly, but if it calls dependacies incorrectly, it still can’t produce the right result.
Seems like a pretty fundamental oversight or hopelessly naive.
November 30th, 2009 at 10:39 am
Thanks for the tip Chris,
I’ve found unit aliases useful for situations where the code has to live in multiple versions of Delphi. The overuse of $IFDEF clutters code needlessly.
Another approach for units which are shared code is to create a unit of the same name in the test suite directory with only the test required functionality. If using this approach or the one you outline, there is the added hazard of keeping both files in sync with each other.
Looks like these default aliases from D2007 are leftover from the D1 to D2 conversion: WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; With these aliases in place, I might be able to compile a D1 project in D2007.