Unit testing with asserts: Why is assertHandler required to throw?

Lutger lutger.blijdestijn at gmail.com
Sun Jan 31 13:01:23 PST 2010


On 01/31/2010 09:39 PM, Trip Volpe wrote:
> I recently began porting an existing C++ project of mine (a compiler/interpreter for a dynamic language) to D. In the process I found that the built-in unit testing support, while an awesome concept, was a little bit sparse. In particular, assert() is fairly useless for unit tests, since it throws on a failure and prevents all subsequent tests from running.
>
> Unfortunately, it's the only facility that seems to give you access to the current file and line number, very important bits of info for unit tests in a any non-trivial project. I was contemplating just abandoning D and sticking with C++ and Googletest, but after doing a bit of digging through the source, I found setAssertHandler() in core.exception. Perfect! I could use my own assert handler that just records the error and allows future asserts to still be reached. So I wrote it up and gave it a try, but pretty quickly started getting access violation errors.
>
> I did a bit of searching, and found a bug report on the issue:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=3208
>
> In the comments it's indicated that this is intentional, that the compiler expects the assertion handler to throw. So I guess I have a few questions:
>
> 1. Why is this the expected behavior? It seems to me that there are relatively few useful things you can do with a custom assert handler unless it is possible to refrain from throwing.
>
> 2. Is this going to be fixed any time soon? It seems fairly important to me; it really kills the value of built-in unit tests to be forced to choose between not having any line number information and being able to recognize only one assert failure per test run(!!!).
>
> 3. Are there any current workarounds for the problem? I could just use my own assertion function, but 1) I can't call it "assert" because that's a reserved word, and 2) most importantly, it won't have any way of indicating in its diagnostic output which source line the failure occurred on. In C++, of course, I could use preprocessor macros to do that, but D omits a preprocessor (for very good reasons). Unfortunately, this seems like a case where D hasn't provided replacement functionality.
>
> But maybe there's a sneaky fix I haven't thought of? I also found Runtime.moduleUnitTester() in core.runtime, which is very useful in itself, but provides an _almost_ useful improvement: I can catch the AssertErrors on a module-by-module basis, which would at least allow the unit tests for all modules to be run regardless of a failure in one of them. However, a single module could easily have dozens of tests in it, each of which could contain many individual asserts. Stopping the test early because one assert failed makes no sense.
>
> It would be _almost_ acceptable to at least be sure of running all the tests in each module; is there any way to poke inside or otherwise override ModuleInfo's unitTest() function?
>
>
> Thanks in advance for any advice or comments! D is a completely awesome language, but I found this issue a bit strange.


You can use line and file info with default arguments, this works 
(special case). I just hack around the default unittest system, 
something like this:

void test(string testName)(void delegate () testClosure,
                            int line = __LINE__,
                            string file = __FILE__)
{
     // register test start, run testClosure, end test
}

void expectEquals(A, B)(A a, B b,
                         int line = __LINE__,
                         string file = __FILE__)
{
     // record outcome of assertion
}

unittest
{
     test!"truth of it"( {
         expectEquals(true, true);
     } );
}

or slightly abusive:

unittest
{
     test!"truth of it" =  {
         expectEquals(true, true);
     };
}



More information about the Digitalmars-d mailing list