www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - assert unittest doesn't respect assertThrown

reply Brother Bill <brotherbill mail.com> writes:
In a unittest, we have assertThrown average([1], [1, 2]));
As assert statement throws an exception.
But the unittest fails, which seems odd, as an assert did thr
Have I found another bug in DMD?

```
import std.stdio : writeln;
import std.exception : enforce, assertThrown, assertNotThrown;

void main()
{
	auto result = average([], []);

	// assert is not caught in catch block
	// enforce is caught in catch block
	try
	{
		result = average([1], [1, 2]);
	}
	catch (Exception e)
	{
		writeln("Caught exception: ", e.msg);
	}	
}

int[] average(int[] a, int[] b)
{
	assert( a.length == b.length, "assert:  Input slices must have 
the same length");	// assert  will not be caught in catch block
	// enforce(a.length == b.length, "enforce: Input slices must 
have the same length");	// enforce will     be caught in catch 
block

	// Implementation goes here
	return cast(int[])[]; // Initial result, so it compiles
}

unittest
{
	/* Must throw for uneven slices */
	assertThrown(average([1], [1, 2]));

	/* Must not throw for empty slices */
	assertNotThrown(average([], []));
}
```

Running it in Linux Terminal
```
bb fedora:~/temp/c42_p217_6a_testing_for_exceptions$ dub test 

              No source files found in configuration 'library'. 
Falling back to default configuration for test runner.
     Starting Performing "unittest" build using /usr/bin/dmd for 
x86_64.
     Building c42_6a_testing_for_exceptions ~master: building 
configuration [application]
      Linking c42_6a_testing_for_exceptions
      Running c42_6a_testing_for_exceptions
core.exception.AssertError source/app.d(22): assert:  Input 
slices must have the same length
----------------
??:? _d_assert_msg [0x40301c]
source/app.d:22 int[] app.average(int[], int[]) [0x400d6e]
source/app.d:32 pure  safe int[] 
app.__unittest_L29_C1().__dgliteral_L32_C22() [0x400e46]
/usr/include/dmd/phobos/std/exception.d:294 pure nothrow  safe 
void std.exception.assertThrown!(Exception, 
int[]).assertThrown(lazy int[], immutable(char)[], 
immutable(char)[], ulong) [0x4025b2]
source/app.d:32 void app.__unittest_L29_C1() [0x400daf]
??:? void app.__modtest() [0x402cbc]
??:? int 
core.runtime.runModuleUnitTests().__foreachbody_L603_C5(object.ModuleInfo*)
[0x40c52e]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)).__lambda_L2519_C13(immutable(object.ModuleInfo*))
[0x40a087]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))).__foreachbody_L585_C5(ref
rt.sections_elf_shared.DSO) [0x412593]
??:? int rt.sections_elf_shared.DSO.opApply(scope int 
delegate(ref rt.sections_elf_shared.DSO)) [0x412be5]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))) [0x412521]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)) [0x40a059]
??:? runModuleUnitTests [0x40c363]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).runAll() [0x403c0c]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).tryExec(scope void delegate()) [0x403b99]
??:? _d_run_main2 [0x403b0f]
??:? _d_run_main [0x403917]
/usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 
main [0x400e99]
??:? [0x7f80e860f5b4]
??:? __libc_start_main [0x7f80e860f667]
??:? _start [0x400b64]
1/1 modules FAILED unittests
Error Program exited with code 1
```
Jan 20
next sibling parent Brother Bill <brotherbill mail.com> writes:
On Tuesday, 20 January 2026 at 20:42:21 UTC, Brother Bill wrote:
 In a unittest, we have assertThrown average([1], [1, 2]));
 As assert statement throws an exception.
 But the unittest fails, which seems odd, as an assert did throw.
 Have I found another bug in DMD?

 ```
 import std.stdio : writeln;
 import std.exception : enforce, assertThrown, assertNotThrown;

 void main()
 {
 	auto result = average([], []);

 	// assert is not caught in catch block
 	// enforce is caught in catch block
 	try
 	{
 		result = average([1], [1, 2]);
 	}
 	catch (Exception e)
 	{
 		writeln("Caught exception: ", e.msg);
 	}	
 }

 int[] average(int[] a, int[] b)
 {
 	assert( a.length == b.length, "assert:  Input slices must have 
 the same length");	// assert  will not be caught in catch block
 	// enforce(a.length == b.length, "enforce: Input slices must 
 have the same length");	// enforce will     be caught in catch 
 block

 	// Implementation goes here
 	return cast(int[])[]; // Initial result, so it compiles
 }

 unittest
 {
 	/* Must throw for uneven slices */
 	assertThrown(average([1], [1, 2]));

 	/* Must not throw for empty slices */
 	assertNotThrown(average([], []));
 }
 ```

 Running it in Linux Terminal
 ```
 bb fedora:~/temp/c42_p217_6a_testing_for_exceptions$ dub test 

              No source files found in configuration 'library'. 
 Falling back to default configuration for test runner.
     Starting Performing "unittest" build using /usr/bin/dmd for 
 x86_64.
     Building c42_6a_testing_for_exceptions ~master: building 
 configuration [application]
      Linking c42_6a_testing_for_exceptions
      Running c42_6a_testing_for_exceptions
 core.exception.AssertError source/app.d(22): assert:  Input 
 slices must have the same length
 ----------------
 ??:? _d_assert_msg [0x40301c]
 source/app.d:22 int[] app.average(int[], int[]) [0x400d6e]
 source/app.d:32 pure  safe int[] 
 app.__unittest_L29_C1().__dgliteral_L32_C22() [0x400e46]
 /usr/include/dmd/phobos/std/exception.d:294 pure nothrow  safe 
 void std.exception.assertThrown!(Exception, 
 int[]).assertThrown(lazy int[], immutable(char)[], 
 immutable(char)[], ulong) [0x4025b2]
 source/app.d:32 void app.__unittest_L29_C1() [0x400daf]
 ??:? void app.__modtest() [0x402cbc]
 ??:? int 
 core.runtime.runModuleUnitTests().__foreachbody_L603_C5(object.ModuleInfo*)
[0x40c52e]
 ??:? int object.ModuleInfo.opApply(scope int 
 delegate(object.ModuleInfo*)).__lambda_L2519_C13(immutable(
bject.ModuleInfo*)) [0x40a087]
 ??:? int rt.minfo.moduleinfos_apply(scope int 
 delegate(immutable(object.ModuleInfo*))).__foreachbody_L585_C5(ref
rt.sections_elf_shared.DSO) [0x412593]
 ??:? int rt.sections_elf_shared.DSO.opApply(scope int 
 delegate(ref rt.sections_elf_shared.DSO)) [0x412be5]
 ??:? int rt.minfo.moduleinfos_apply(scope int 
 delegate(immutable(object.ModuleInfo*))) [0x412521]
 ??:? int object.ModuleInfo.opApply(scope int 
 delegate(object.ModuleInfo*)) [0x40a059]
 ??:? runModuleUnitTests [0x40c363]
 ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) 
 int function(char[][])*).runAll() [0x403c0c]
 ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) 
 int function(char[][])*).tryExec(scope void delegate()) 
 [0x403b99]
 ??:? _d_run_main2 [0x403b0f]
 ??:? _d_run_main [0x403917]
 /usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 
 main [0x400e99]
 ??:? [0x7f80e860f5b4]
 ??:? __libc_start_main [0x7f80e860f667]
 ??:? _start [0x400b64]
 1/1 modules FAILED unittests
 Error Program exited with code 1
 ```
Jan 20
prev sibling parent reply =?UTF-8?Q?Ali_=C3=87ehreli?= <acehreli yahoo.com> writes:
On 1/20/26 12:42 PM, Brother Bill wrote:
 In a unittest, we have assertThrown average([1], [1, 2]));
 As assert statement throws an exception.
Yes, throws an exception but not Exception.
 But the unittest fails, which seems odd, as an assert did thr
 Have I found another bug in DMD?
I happen to have DMD64 D Compiler v2.109.1 on this environment. Your unittest passes. What fails is the code inside 'main'.
 ```
 import std.stdio : writeln;
 import std.exception : enforce, assertThrown, assertNotThrown;

 void main()
 {
      auto result = average([], []);

      // assert is not caught in catch block
      // enforce is caught in catch block
      try
      {
          result = average([1], [1, 2]);
      }
      catch (Exception e)
      {
          writeln("Caught exception: ", e.msg);
      }
 }
There is the issue: The exception type that assert throws is *not* under the Exception hierarchy but under the Error hierarchy: ``` Throwable (not recommended to catch) ↗ ↖ Exception Error (not recommended to catch) ↗ ↖ ↗ ↖ ... ... ... ... ``` You could catch Throwable or Error. However, as they are not recommended to be caught, perhaps a better option is to change assert() to enforce(). enforce() throws a type under the Exception hierarchy. Ali
Jan 20
next sibling parent Brother Bill <brotherbill mail.com> writes:
On Tuesday, 20 January 2026 at 21:58:46 UTC, Ali Çehreli wrote:
 What fails is the code inside 'main'.

 ```
 import std.stdio : writeln;
 import std.exception : enforce, assertThrown, assertNotThrown;

 void main()
 {
      auto result = average([], []);

      // assert is not caught in catch block
      // enforce is caught in catch block
      try
      {
          result = average([1], [1, 2]);
      }
      catch (Exception e)
      {
          writeln("Caught exception: ", e.msg);
      }
 }
There is the issue: The exception type that assert throws is *not* under the Exception hierarchy but under the Error hierarchy: ``` Throwable (not recommended to catch) ↗ ↖ Exception Error (not recommended to catch) ↗ ↖ ↗ ↖ ... ... ... ... ``` You could catch Throwable or Error. However, as they are not recommended to be caught, perhaps a better option is to change assert() to enforce(). enforce() throws a type under the Exception hierarchy. Ali
That is true. I got rid of the try catch, with only the try body, and the unittest passes. Ali, I am still working on the D Udemy course. It will likely be over 40 hours of tutorial video, based on your book. When released in the Spring, it should make it easy for newbies to master learning the D language, which is not trivial. I am working on it Full Time. Hopefully, this will put D language back in the "Real" language category. -- Brother Bill
Jan 20
prev sibling parent reply Brother Bill <brotherbill mail.com> writes:
On Tuesday, 20 January 2026 at 21:58:46 UTC, Ali Çehreli wrote:
 On 1/20/26 12:42 PM, Brother Bill wrote:
 In a unittest, we have assertThrown average([1], [1, 2]));
 As assert statement throws an exception.
Yes, throws an exception but not Exception.
 But the unittest fails, which seems odd, as an assert did thr
 Have I found another bug in DMD?
I happen to have DMD64 D Compiler v2.109.1 on this environment. Your unittest passes. What fails is the code inside 'main'.
 ```
 import std.stdio : writeln;
 import std.exception : enforce, assertThrown, assertNotThrown;

 void main()
 {
      auto result = average([], []);

      // assert is not caught in catch block
      // enforce is caught in catch block
      try
      {
          result = average([1], [1, 2]);
      }
      catch (Exception e)
      {
          writeln("Caught exception: ", e.msg);
      }
 }
There is the issue: The exception type that assert throws is *not* under the Exception hierarchy but under the Error hierarchy: ``` Throwable (not recommended to catch) ↗ ↖ Exception Error (not recommended to catch) ↗ ↖ ↗ ↖ ... ... ... ... ``` You could catch Throwable or Error. However, as they are not recommended to be caught, perhaps a better option is to change assert() to enforce(). enforce() throws a type under the Exception hierarchy. Ali
Just curious as to why the unittest failed. After all, the assert was thrown. Is it because the try - catch block has an assert, but the catch block didn't catch it, which crashed the program before the unittest completed?
Jan 20
parent reply =?UTF-8?Q?Ali_=C3=87ehreli?= <acehreli yahoo.com> writes:
On 1/20/26 3:04 PM, Brother Bill wrote:

 Just curious as to why the unittest failed.  After all, the assert was
 thrown.
I don't think there was an issue there.
 Is it because the try - catch block has an assert, but the catch block
 didn't catch it
Yes. Your catch was for Exception but the exception type thrown by assert is not under the Exception hierarchy (it is an Error, not Exception).
 which crashed the program before the unittest completed?
unittest blocks are executed before 'main' is entered. So, unittest blocks worked correctly but the program terminated with an uncaught exception. Ali
Jan 21
parent Brother Bill <brotherbill mail.com> writes:
The issue was that average always threw an exception.

Here is the "final" working code, which passes the unit tests.

source/app.d
```
import std.stdio : writeln;
import std.exception : assertThrown, assertNotThrown;

void main()
{

}

class UnequalLengths : Exception
{
	this(string msg, string file = __FILE__, size_t line = __LINE__)
	{
		super(msg, file, line);
		writeln("Unequal lengths");
	}
}

class RangeError : Exception
{
	this(string msg, string file = __FILE__, size_t line = __LINE__)
	{
		super(msg, file, line);
		writeln("Range error");
	}
}

int[] average(int[] a, int[] b)
{
	if (a.length != b.length)
	{
		throw new UnequalLengths("Unequal lengths");
	}

	int[] result;
	result.length = a.length;
	result[] = (a[] + b[]) / 2;
	return result;
}

unittest
{
	// Must throw UnequalLengths for uneven slices
	assertThrown!UnequalLengths(average([1], [1, 2]));
	/* The equivalent of the line above */
	/+
	{
		auto isUnequalLengthsThrown = false;
		try
		{
			average([1], [1, 2]);
		}
		catch (UnequalLengths exc)
		{
			isUnequalLengthsThrown = true;
		}
		assert(isUnequalLengthsThrown == true);
	}
	+/

	// Must not throw RangeError for empty slices (it may throw 
other types of exceptions)
	assertNotThrown!RangeError(average([], []));
	/* The equivalent of the line above */
	/+
	{
		auto inRangeErrorThrown = false;
		try
		{
			average([1], [1, 2]);
		}
		catch (RangeError exc)
		{
			inRangeErrorThrown = true;
		}
		catch (Exception exc)
		{
			// do nothing
		}
		assert(inRangeErrorThrown == false);
	}
	+/
}

```
Jan 21