www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Custom exceptions violate unittest

reply Brother Bill <brotherbill mail.com> writes:
In book "Programming in D", on page 217.
My concern is why the assertThrown!UnequalLengths unit test does 
not catch the custom Exception of UnequalLengths.

Is this a bug or a feature?

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("Unequal lengths");
	}
}

void average(int[] a, int[] b)
{
	throw new UnequalLengths("Unequal lengths");
}

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

	// Must not throw RangeError for empty slices (it may throw 
other types of exceptions)
	assertNotThrown!RangeError(average([], []));
}

```

I would expect that both unit tests would pass and that the unit 
test not crash, which it does.

Console output:
```
 dub test
No source files found in configuration 'library'. Falling back to default configuration for test runner. Starting Performing "unittest" build using C:\D\dmd2\windows\bin64\dmd.exe for x86_64. Building c42_6b_testing_for_exceptions_custom_exceptions ~master: building configuration [application] Linking c42_6b_testing_for_exceptions_custom_exceptions Running c42_6b_testing_for_exceptions_custom_exceptions.exe Unequal lengths Unequal lengths app.UnequalLengths source\app.d(29): Unequal lengths ---------------- 0x00007FF74E1E2967 in d_throwc 0x00007FF74E1DF034 in app.average at C:\temp\c42_p217_6b_testing_for_exceptions_custom_exceptions\source\app.d(29) 0x00007FF74E1DF23D in app.__unittest_L32_C1.__dgliteral_L38_C36 at C:\temp\c42_p217_6b_testing_for_exceptions_custom_exceptions\source\app.d(38) 0x00007FF74E1E16FB in std.exception.assertNotThrown!(app.RangeError, void).assertNotThrown at C:\D\dmd2\windows\bin64\..\..\src\phobos\std\exception.d(149) 0x00007FF74E1DF107 in app.__unittest_L32_C1 at C:\temp\c42_p217_6b_testing_for_exceptions_custom_exceptions\source\app.d(38) 0x00007FF74E1E19BD in void app.__modtest() 0x00007FF74E1F072E in int core.runtime.runModuleUnitTests().__foreachbody_L603_C5(object.ModuleInfo*) 0x00007FF74E1E8827 in int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)).__lambda_L2467_C13(immutable(object.ModuleInfo*)) 0x00007FF74E1EDBC4 in int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))).__foreachbody_L582_C5(ref rt.sections_win64.SectionGroup) 0x00007FF74E1EE39C in int rt.sections_win64.SectionGroup.opApply(scope int delegate(ref rt.sections_win64.SectionGroup)) 0x00007FF74E1EDB2B in int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))) 0x00007FF74E1E87E7 in int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) 0x00007FF74E1F0550 in runModuleUnitTests 0x00007FF74E1EA975 in void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() 0x00007FF74E1EA8CF in void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) 0x00007FF74E1EA69A in d_run_main2 0x00007FF74E1E32C9 in d_run_main 0x00007FF74E1DF282 in app._d_cmain!().main at C:\D\dmd2\windows\bin64\..\..\src\druntime\import\core\internal\entrypoint.d(29) 0x00007FF74E26652C in __scrt_common_main_seh at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl(288) 0x00007FFA38EFE8D7 in BaseThreadInitThunk 0x00007FFA3A78C53C in RtlUserThreadStart 1/1 modules FAILED unittests Error Program exited with code 1 ```
Nov 30
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 30 November 2025 at 14:00:12 UTC, Brother Bill wrote:
 In book "Programming in D", on page 217.
 My concern is why the assertThrown!UnequalLengths unit test 
 does not catch the custom Exception of UnequalLengths.
I just tried building and running your example program with both DMD 2.110.0 and LDC 1.40.1, and it worked correctly both times.
Nov 30
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Sunday, 30 November 2025 at 14:00:12 UTC, Brother Bill wrote:
 In book "Programming in D", on page 217.
 My concern is why the assertThrown!UnequalLengths unit test 
 does not catch the custom Exception of UnequalLengths.

 Is this a bug or a feature?

 source/app.d
 ```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("Unequal lengths");
 	}
 }

 void average(int[] a, int[] b)
 {
 	throw new UnequalLengths("Unequal lengths");
 }

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

 	// Must not throw RangeError for empty slices (it may throw 
 other types of exceptions)
 	assertNotThrown!RangeError(average([], []));
 }

 ```
It may throw other exceptions. But those exceptions are *not caught* by `assertNotThrown`. From the docs:
 Asserts that the given expression does not throw the given type 
 of Throwable. If a Throwable of the given type is thrown, it is 
 caught and does not escape assertNotThrown. Rather, an 
 AssertError is thrown. **However, any other Throwables will 
 escape.**
I can imagine a scenario where you have an exception that is a derivative of another exception. Let's say I wanted to assert that an `Exception` is thrown, but that it is not an `UnequalLengths` exception. Then you would do: ```d assertThrown!Exception(assertNotThrown!UnequalLengths(...)); ``` But other than that, specifying that a specific exception is not thrown seems to be of very limited value. FWIW, I'd change the message in those two writelns so you can tell which one is written. -Steve
Nov 30