DUnit tricks: Getting stack traces for test failures
DUnit has an option to use JCL to generate stack traces. The idea is that, whenever there’s an exception or a test failure, DUnit will show a stack trace right along with the failure message. The only problem is, it doesn’t work.
There’s no problem getting stack traces for unexpected exceptions. This is really useful; you get the address, unit name, method name, and even the line number of the line that threw an exception, together with the call stack that led to that code getting called. Immensely useful.
The problem is, you don’t get the same thing for test failures — even though Fail and CheckEquals, and even DUnitLite’s Specify.That, operate by throwing exceptions (they’ve got their own exception class, ETestFailure). You should be able to get a stack trace that shows the exact line of code that contained the failing assertion. In fact, we’re using older versions of DUnit and JCL at work, and we get stack traces just fine.
Unfortunately, stack traces for test failures are broken by default in the latest versions of DUnit and JCL. But there’s hope — notice that I said “by default”. Below, I’ll tell you how to fix that default.
Enabling DUnit stack tracing
First of all, here’s how to get DUnit to show stack traces in the first place.
You’ll need to download the source code for both DUnit and the Jedi Code Library. (Recent versions of Delphi ship with an older version of DUnit, but I only tested this with the latest version.)
Add the DUnit, JCL, JCL\Common, and JCL\Windows directories to your project’s search path.
Then make the following changes in Project > Options:
- On the Directories/Conditionals page, set “Conditional defines” to:
USE_JEDI_JCL - On the Linker page, set “Map file” to “Detailed”.
Now write a test that throws an unexpected exception, compile, and run. Here’s a contrived example, but it’ll give you an idea of what it looks like:
Enabing stack tracing for test failures
We don’t get unexpected exceptions in our tests very often. More often, it’s test failures. And when we have more than one assertion in the same test (not the ideal, but it happens a lot), sometimes it’s hard to know which assertion failed. Or rather, it’s always easy to know, if you have stack traces.
The problem is with JCL’s exclusion list. The latest version of JCL keeps a configurable list of exception types that it shouldn’t generate stack traces for. Seems like a reasonable feature. But the JCL and DUnit teams made three important design decisions, at various points in time:
- JCL’s exclusion list, by default, contains one class:
EAbort. - JCL ignores not just the classes in the exclusion list, but any of their descendant classes as well.
- DUnit’s
ETestFailuredescends from… yep, you guessed it,EAbort.
Put all three together, and stuff doesn’t work.
But, all that said, it’s easy to work around. Just add the following code to your project file, before you call one of the RunRegisteredTests routines:
uses ..., JclDebug, ...; begin ... JclDebug.RemoveIgnoredException(EAbort); ... end.
And bask in the goodness that is stack traces.

April 17th, 2008 at 5:41 am
Is it possible to get the full file name (file name + path) in the stack trace?
It would be really nice, if one could just double click the failures and be taken to the exact line in the Delphi IDE.
I’d like to add this to the TGUIRunner, but I just can’t get the full file path.
Best regards,
Ajasja
April 22nd, 2008 at 6:17 am
No, there doesn’t seem to be a way to do that. Even if I compile with TD32 debug info, which is supposed to have even more info than the detailed .MAP file, I don’t get file paths in the stack trace. And if those paths were in the TD32 debug info, I’m pretty sure the JCL would be able to extract them.
You’ll probably have to keep a list of directories to search through. There doesn’t seem to be any better way.
April 25th, 2008 at 8:44 am
I did find a partial solution:
I use SendKeys (http://delphi.about.com/od/adptips2004/a/bltip1104_3.htm) to activate delphi and send CTRL+F12 (view unit) and then the unit name + ENTER.
So the unit has to be part of the project, but otherwise it works quite nicely. I did have some problems with the editor losing focus, but the new version of DDevExtension (http://andy.jgknet.de/dspeedup/index.php?page=DDevExtensions) solves that. I do have the unit selector turned of though.
Not the most elegant, but it works
Here’s the code extract:
var
DelphiHandle : hwnd;
DelphiHandle:=FindWindow(’TAppBuilder’,nil);
AppActivate(DelphiHandle);
SendKeys(’^{F12}’,true); //Ctrl+F12 is View Unit
SendKeys(Pchar(aUnitName),true); //look up unit name
SendKeys(’~',true); //enter
SendKeys(Pchar(’%g’),true); //Alt+G is the goto line dialog
SendKeys(Pchar(IntTostr(aLineNum)),true); //send line
SendKeys(’~',true); //enter