Are exceptions caught in unittests?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Fri Feb 16 08:48:08 UTC 2024


On Friday, February 16, 2024 1:06:26 AM MST Ferhat Kurtulmuş via Digitalmars-
d-learn wrote:
> On Friday, 16 February 2024 at 07:43:24 UTC, Ferhat Kurtulmuş
>
> wrote:
> > When I tried to catch exceptions in my unit test, I found that
> > exceptions were not thrown or caught in the unit test blocks.
> > So, I cannot use assertThrown at all. Is this a bug or expected
> > behavior that I don't know?
> >
> > Using LDC 1.36.0 on Windows.
> >
> > https://github.com/aferust/evalex/blob/main/source/evalex.d#L694
>
> Even a test code like this does not work:
>
> ```d
> string division(int a, int b) {
>     string result = "";
>
>     try {
>        if( b == 0 ) {
>           throw new Exception("Cannot divide by zero!");
>        } else {
>           result = format("%s",a/b);
>        }
>     } catch (Exception e) {
>        result = e.msg;
>     }
>
>     return result;
> }
>
> assertThrown({division(50, 0);});
> ```

1. assertThrown does not test whether something somewhere in what you called
threw an exception. It asserts that it catches an exception. Your example
here is catching the exception and returning so the exception never escapes
the division function, and there's nothing for assertThrown to catch.

2. You're just passing a lambda to assertThrown without actually calling it.
The first argument to assertThrown is lazy, which means that it creates a
delegate from what you pass to it. assertThrown then calls that delegate.
However, in your example here, that delegate just contains a lambda
declaration, so it would basically be the equivalent of having

void foo()
{
    {division(50, 0);};
}

which would be like doing

void foo()
{
    int i;
}

in the sense that the function just contains a declaration that does
nothing.

The normal thing to do here would be to not use a lambda at all, since it
just contains a function call.

    assertThrown(division(50, 0));

but in cases where you actually need a lambda, because you're doing
something more complex than simply calling a function, you need to call it
when passing it. e.g.

    assertThrown({division(50, 0);}());

That then would be like declaring

void foo()
{
    {division(50, 0);}();
}

so when assertThrown calls the delegate, the delegate actually calls the
lambda. But regardless, the expression that you pass to assertThrown has to
actually throw an Exception if you want it to pass, and division doesn't do
that, because it catches the Exception internally.

If what you want to test is whether something deeper down the stack threw an
Exception that got caught, then you're out of luck. That's the sort of thing
where you'd need a debugger or some other tool that tracked what the code
was doing. assertThrown is just a simple library function that helps you
test that an expression throws an exception - generally with the idea that
it allows you to test that a piece of code throws an exception when it's
suposed to (e.g. because you gave it bad input).

void assertThrown(T : Throwable = Exception, E)
                 (lazy E expression,
                  string msg = null,
                  string file = __FILE__,
                  size_t line = __LINE__)
{
    import core.exception : AssertError;

    try
        expression();
    catch (T)
        return;

    static if (!is(immutable E == immutable noreturn))
        throw new AssertError("assertThrown failed: No " ~ T.stringof ~
                              " was thrown" ~
                              (msg.length == 0 ? "." : ": ") ~ msg,
                          file, line);
}

- Jonathan M Davis






More information about the Digitalmars-d-learn mailing list