www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - There must be module with test assertions in Phobos

reply Dmytro Katyukha <firemage.dima gmail.com> writes:
Hi all,

D is cool language. It has a lot of good features.
One of such features is integrated unittests.

But, this feature is not convenient/usable without standard set 
of `assert*` methods, that could display human-readable output in 
case of error (what value expected, and what value we got).
Because of absense of such expected functionality, there are some 
set of third-party libraries that implement good and readable 
assertions. But, most popular assertion libraries depend on 
[unit-threaded](https://code.dlang.org/packages/unit-threaded) 
that is large test runner.

And it is strange, when you try to add small and simple library 
to dependencies of your project, but it brings whole 
unit-threaded package to your project. And i think, in 
significant amount of cases, this dependency is added just 
because standard set of asserts absent in standard library.

I think, for small libraries there is no sense to depend on large 
test runner. Ususally it is enough to use standard test runner.

Just take a look at python's assertions from standard library 
[unittest](https://docs.python.org/3/library/unittest.html#assert-methods)

I think, it could be enough to have following asserts in stadard 
library:
- assertEqual
- assertNotEqual
- assertTrue
- assertFalse
- assertIn
- assertNotIn
- assertThrows
- assertGreater
- assertGreaterEqual
- assertLessEqual

I think, implementation of this list of asserts in the Phobos 
will allow users to start use unittest much easier.

Also, the question to authors of assert libraries (like 
[unit-threaded:assertions](https://code.dlang.org/packages/unit-thr
aded%3Aassertions), [dshould](https://code.dlang.org/packages/dshould), etc):
what do you think about contributing assertions to Phobos?

Just example of why standard `assert` is not enough.

Assume, that we have simple app with unittests like below:

```d
import std.stdio;

unittest {
     int x = 0, y = 5;
     assert(x == y, "X != Y");
}

unittest {
     import dshould;

     int x = 0, y = 5;
     x.should.equal(y);
}

unittest {
     import unit_threaded.assertions;

     int x = 0, y = 5;
     x.should == y;
}

void main()
{
	writeln("Edit source/app.d to start your project.");
}
```

And let's check output of assertions:

The first, standard assertion could look like below. As you can 
see, there is no info about values of `x` and `y`.

```
core.exception.AssertError source/app.d(5): X != Y
----------------
??:? _d_unittest_msg [0x5596a817de88]
source/app.d:5 void app.__unittest_L3_C1() [0x5596a81578ef]
??:? void app.__modtest() [0x5596a8162ee0]
??:? int 
core.runtime.runModuleUnitTests().__foreachbody6(object.ModuleInfo*)
[0x5596a8188ae6]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)).__lambda2(immutable(object.ModuleInfo*))
[0x5596a8174c8b]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))).__foreachbody2(ref 
rt.sections_elf_shared.DSO) [0x5596a81827ff]
??:? int rt.sections_elf_shared.DSO.opApply(scope int 
delegate(ref rt.sections_elf_shared.DSO)) [0x5596a8182989]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))) [0x5596a818278d]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)) [0x5596a8174c5d]
??:? runModuleUnitTests [0x5596a818891b]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).runAll() [0x5596a817f32c]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).tryExec(scope void delegate()) 
[0x5596a817f2b9]
??:? _d_run_main2 [0x5596a817f222]
??:? _d_run_main [0x5596a817f00b]
/usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 
main [0x5596a8157a11]
??:? [0x7fbc5aa2350f]
??:? __libc_start_main [0x7fbc5aa235c8]
??:? _start [0x5596a81577e4]
1/1 modules FAILED unittests
```

[dshould](https://code.dlang.org/packages/dshould) will print 
following traceback, that has info about exact values of `x` and 
`y`.

```
dshould.ShouldType.FluentErrorImpl!(unit_threaded.exception.UnitTestError).FluentErrorIm
l source/app.d(12): Test failed: expected 5, but got 0
----------------
/home/katyukha/.dub/packages/dshould-1.7.1/dshould/src/dshould/ShouldType.d:127
pure nothrow  safe void dshould.ShouldType.ShouldType!(int delegate() pure
 safe, ["equal"]).ShouldType.check(bool, lazy immutable(char)[], lazy
immutable(char)[], immutable(char)[], ulong) [0x55b3b74d6fb5]
/home/katyukha/.dub/packages/dshould-1.7.1/dshould/src/dshould/basic.d:359 pure
 safe void dshould.basic.numericCheck!(dshould.ShouldType.ShouldType!(int
delegate() pure  safe, ["equal"]).ShouldType,
int).numericCheck(dshould.ShouldType.ShouldType!(int delegate() pure  safe,
["equal"]).ShouldType, const(int), immutable(char)[], ulong) [0x55b3b74d7e70]
/home/katyukha/.dub/packages/dshould-1.7.1/dshould/src/dshould/basic.d:197 pure
 safe void dshould.basic.equal!(dshould.ShouldType.ShouldType!(int delegate()
pure  safe, []).ShouldType, int).equal(dshould.ShouldType.ShouldType!(int
delegate() pure  safe, []).ShouldType, int, dshould.ShouldType.Fence,
immutable(char)[], ulong) [0x55b3b74d6bc1]
/home/katyukha/.dub/packages/dshould-1.7.1/dshould/src/dshould/package.d:85
pure  safe void dshould.equal!(dshould.ShouldType.ShouldType!(int delegate()
pure  safe, []).ShouldType, int).equal(dshould.ShouldType.ShouldType!(int
delegate() pure  safe, []).ShouldType, int, dshould.ShouldType.Fence,
immutable(char)[], ulong) [0x55b3b74d6963]
source/app.d:12 void app.__unittest_L8_C1() [0x55b3b74d4919]
??:? void app.__modtest() [0x55b3b74dfe98]
??:? int 
core.runtime.runModuleUnitTests().__foreachbody6(object.ModuleInfo*)
[0x55b3b7505a06]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)).__lambda2(immutable(object.ModuleInfo*))
[0x55b3b74f1c3b]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))).__foreachbody2(ref 
rt.sections_elf_shared.DSO) [0x55b3b74ff71f]
??:? int rt.sections_elf_shared.DSO.opApply(scope int 
delegate(ref rt.sections_elf_shared.DSO)) [0x55b3b74ff8a9]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))) [0x55b3b74ff6ad]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)) [0x55b3b74f1c0d]
??:? runModuleUnitTests [0x55b3b750583b]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).runAll() [0x55b3b74fc24c]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).tryExec(scope void delegate()) 
[0x55b3b74fc1d9]
??:? _d_run_main2 [0x55b3b74fc142]
??:? _d_run_main [0x55b3b74fbf2b]
/usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 
main [0x55b3b74d49c9]
??:? [0x7fca6e82350f]
??:? __libc_start_main [0x7fca6e8235c8]
??:? _start [0x55b3b74d47e4]
1/1 modules FAILED unittests
```

And finally, 
[unit-threaded:assertions](https://code.dlang.org/packages/unit-th
eaded%3Aassertions) will print info about exact values of variables compared in
assertion:

```
unit_threaded.exception.UnitTestException source/app.d(19): 
Expected: 5
      Got: 0
----------------
/home/katyukha/.dub/packages/unit-threaded-2.1.5/unit-threaded/subpackages/assertions/source/unit_thre
ded/assertions.d:56 pure  safe void unit_threaded.assertions.shouldEqual!(int,
int).shouldEqual(ref int, ref int, immutable(char)[], ulong) [0x555c04e1ba7a]
/home/katyukha/.dub/packages/unit-threaded-2.1.5/unit-threaded/subpackages/assertions/source/unit_thread
d/assertions.d:1074 pure  safe bool
unit_threaded.assertions.should!(int).should(ref
int).Should.opEquals!(int).opEquals(ref int, immutable(char)[], ulong)
[0x555c04e1b9ac]
source/app.d:19 void app.__unittest_L15_C1() [0x555c04e198f6]
??:? void app.__modtest() [0x555c04e1c43c]
??:? int 
core.runtime.runModuleUnitTests().__foreachbody6(object.ModuleInfo*)
[0x555c04e34822]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)).__lambda2(immutable(object.ModuleInfo*))
[0x555c04e20cb3]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))).__foreachbody2(ref 
rt.sections_elf_shared.DSO) [0x555c04e2d403]
??:? int rt.sections_elf_shared.DSO.opApply(scope int 
delegate(ref rt.sections_elf_shared.DSO)) [0x555c04e2d58d]
??:? int rt.minfo.moduleinfos_apply(scope int 
delegate(immutable(object.ModuleInfo*))) [0x555c04e2d391]
??:? int object.ModuleInfo.opApply(scope int 
delegate(object.ModuleInfo*)) [0x555c04e20c85]
??:? runModuleUnitTests [0x555c04e34657]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).runAll() [0x555c04e29f30]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int 
function(char[][])*).tryExec(scope void delegate()) 
[0x555c04e29ebd]
??:? _d_run_main2 [0x555c04e29e26]
??:? _d_run_main [0x555c04e29c0f]
/usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 
main [0x555c04e19939]
??:? [0x7fa059e2350f]
??:? __libc_start_main [0x7fa059e235c8]
??:? _start [0x555c04e197e4]
1/1 modules FAILED unittests
```

I think, ideally, test assertions should display only the file 
and line with failed assertions, because all other part of 
traceback with D internals usually is not needed to fix the test, 
but the exact values of variable compared in assert are required.

It is possible, to get similar output with standard assert using 
`format`:

```d
unittest {
     import std.format: format;
     int x = 0, y = 5;
     assert(x == y, "%s != %s".format(x, y));
}
```

But in case, if `x` and / or `y` is expression, the unittest 
becomes unreadable.

So, what do you think about this?
Mar 30 2023
next sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha wrote:
 Hi all,
 [..]
Hi! Are you aware of the `-checkaction=context` compiler flag? Give it a shot: ```d unittest { int x = 0, y = 5; assert(x == y); } ``` ```sh dmd -main -unittest -checkaction=context -run ./example.d ``` ``` onlineapp.d(3): [unittest] 0 != 5 1/1 modules FAILED unittests ``` Test on [run.dlang.io][0]. This is not an argument against a dedicated assertion module, but I think it's good to be aware what can be achieved at the compiler/runtime level vs library level. [0]: https://run.dlang.io/?compiler=dmd&source=unittest%20%7B%0A%20%20%20%20int%20x%20%3D%200,%20y%20%3D%205;%0A%20%20%20%20assert(x%20%3D%3D%20y);%0A%7D%0A&args=-unittest%20-checkaction%3Dcontext%20-main
Mar 30 2023
next sibling parent reply Dmytro Katyukha <firemage.dima gmail.com> writes:
On Thursday, 30 March 2023 at 17:22:39 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha 
 wrote:
 Hi all,
 [..]
Hi! Are you aware of the `-checkaction=context` compiler flag? Give it a shot: ```d unittest { int x = 0, y = 5; assert(x == y); } ``` ```sh dmd -main -unittest -checkaction=context -run ./example.d ``` ``` onlineapp.d(3): [unittest] 0 != 5 1/1 modules FAILED unittests ``` Test on [run.dlang.io][0]. This is not an argument against a dedicated assertion module, but I think it's good to be aware what can be achieved at the compiler/runtime level vs library level. [0]: https://run.dlang.io/?compiler=dmd&source=unittest%20%7B%0A%20%20%20%20int%20x%20%3D%200,%20y%20%3D%205;%0A%20%20%20%20assert(x%20%3D%3D%20y);%0A%7D%0A&args=-unittest%20-checkaction%3Dcontext%20-main
Thanks for information, i was not aware about this compiler switch. Also, i would like to note that, there is no info about this compiler switch in [unittests documentation](https://dlang.org/spec/unittest.html)
Mar 30 2023
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 30 March 2023 at 19:53:42 UTC, Dmytro Katyukha wrote:
 [..]
 Also, i would like to note that, there is no info about this 
 compiler switch in [unittests 
 documentation](https://dlang.org/spec/unittest.html)
All dmd compiler switches are documented on this page: [dlang.org/dmd](https://dlang.org/dmd). This one specifically, here: [dlang.org/dmd#switch-checkaction](https://dlang.org/dmd#switch-checkaction). However, I agree that the documentation doesn't provide sufficient information and that it should also be linked from the [spec/unittest](https://dlang.org/spec/unittest) page. Right now, probably the best documentation is on the dmd v2.085 changelog page, when it was first introduced: [dlang.org/changelog/2.085.0#assert](https://dlang.org/changelog/2.085.0#assert).
Mar 31 2023
parent reply David Gileadi <gileadisNOSPM gmail.com> writes:
On 3/31/23 12:46 AM, Petar Kirov [ZombineDev] wrote:
 On Thursday, 30 March 2023 at 19:53:42 UTC, Dmytro Katyukha wrote:
 [..]
 Also, i would like to note that, there is no info about this compiler 
 switch in [unittests documentation](https://dlang.org/spec/unittest.html)
All dmd compiler switches are documented on this page: [dlang.org/dmd](https://dlang.org/dmd). This one specifically, here: [dlang.org/dmd#switch-checkaction](https://dlang.org/dmd#switch-checkaction). However, I agree that the documentation doesn't provide sufficient information and that it should also be linked from the [spec/unittest](https://dlang.org/spec/unittest) page. Right now, probably the best documentation is on the dmd v2.085 changelog page, when it was first introduced: [dlang.org/changelog/2.085.0#assert](https://dlang.org/changelog/2.085.0#assert).
Perhaps it should also get enabled by default when the -unittest flag is passed.
Mar 31 2023
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 31 March 2023 at 14:48:01 UTC, David Gileadi wrote:
 [snip]
 Perhaps it should also get enabled by default when the 
 -unittest flag is passed.
Eh, it isn't perfect since it replaces the identifier with their values. The result would have a little more detail. I've been using mir-algorithm's custom assertations lately [1], but unit-threaded [2] is another alternative that Atila built. I don't know off hand is unit-threaded works in nogc code (mir's can). [1] https://github.com/libmir/mir-algorithm/blob/master/source/mir/test.d [2] https://code.dlang.org/packages/unit-threaded
Mar 31 2023
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On Thursday, 30 March 2023 at 17:22:39 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha 
 wrote:
 Hi all,
 [..]
Hi! Are you aware of the `-checkaction=context` compiler flag?
Be aware that this feature has linking issues. https://issues.dlang.org/show_bug.cgi?id=19937 https://issues.dlang.org/show_bug.cgi?id=22374 https://issues.dlang.org/show_bug.cgi?id=22902
Mar 31 2023
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha wrote:
 I think, it could be enough to have following asserts in 
 stadard library:
 - `assertEqual`
 - `assertNotEqual`
 - `assertTrue`
 - `assertFalse`
 - `assertIn`
 - `assertNotIn`
 - `assertThrows`
 - `assertGreater`
 - `assertGreaterEqual`
 - `assertLessEqual`
I’d say most are superfluous: - `assertEqual(a, b)` why not use `assert(a == b)`? - `assertNotEqual(a, b)` why not use `assert(a != b)`? - `assertTrue(cond)` why not use `assert(cond)`? - `assertFalse(cond)` why not use `assert(!cond)`? - `assertGreater` why not use `assert(a > b)`? - `assertGreaterEqual` why not use `assert(a >= b)`? - `assertLessEqual` why not use `assert(a <= b)`? These have non-trivial, but straightforward lowerings: - `assertIn` why not use `canFind` (or `any`) in `std.algorithm.searching`? - `assertNotIn` why not use `!canFind` (or `!any`)? This one is the only one that has value for DRY: - `assertThrows` Instead of ```d assertThrows!Exception(expression); ``` one could write ```d try { expression(); assert(0); } catch (Exception) {} ``` but I see that it is a non-trivial pattern and `assertThrows` just documents clearly what is expected. Even this isn’t that much. Those functions can provide better error messages, but to be honest, the D compiler’s unittest failure messages should be improved instead. The “superfluous” list is just an enumeration of expressions commonly found in `assert` expressions that should have special treatment by the compiler. For `assertThrows`, maybe it could even be worth adding it as a primitive: ```d assert throw(ExceptionType, expression, message) ``` The message is optional, of course. A `static assert` version should exist as well.
Apr 04 2023
next sibling parent reply max haughton <maxhaton gmail.com> writes:
On Tuesday, 4 April 2023 at 12:11:28 UTC, Quirin Schroll wrote:
 On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha 
 wrote:
 [...]
I’d say most are superfluous: - `assertEqual(a, b)` why not use `assert(a == b)`? - `assertNotEqual(a, b)` why not use `assert(a != b)`? - `assertTrue(cond)` why not use `assert(cond)`? - `assertFalse(cond)` why not use `assert(!cond)`? - `assertGreater` why not use `assert(a > b)`? - `assertGreaterEqual` why not use `assert(a >= b)`? - `assertLessEqual` why not use `assert(a <= b)`? [...]
They are redundant but they allow some slightly nicer failure messages upon failure (i.e. assert fail is not very helpful unless you are at one with all the code) It may be of note that Atila has implemented most (all?) of this in unit_threaded.
Apr 04 2023
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Tuesday, 4 April 2023 at 15:45:55 UTC, max haughton wrote:
 They are redundant but they allow some slightly nicer failure 
 messages upon failure (i.e. assert fail is not very helpful 
 unless you are at one with all the code)
I tend to use -checkaction=context together with -unitest...
Apr 04 2023
parent reply max haughton <maxhaton gmail.com> writes:
On Tuesday, 4 April 2023 at 16:01:17 UTC, Adam D Ruppe wrote:
 On Tuesday, 4 April 2023 at 15:45:55 UTC, max haughton wrote:
 They are redundant but they allow some slightly nicer failure 
 messages upon failure (i.e. assert fail is not very helpful 
 unless you are at one with all the code)
I tend to use -checkaction=context together with -unitest...
That too. Not massively in love with it for (say) approximately equal testing floats but it's there.
Apr 04 2023
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 4 April 2023 at 18:50:11 UTC, max haughton wrote:
 On Tuesday, 4 April 2023 at 16:01:17 UTC, Adam D Ruppe wrote:
 On Tuesday, 4 April 2023 at 15:45:55 UTC, max haughton wrote:
 They are redundant but they allow some slightly nicer failure 
 messages upon failure (i.e. assert fail is not very helpful 
 unless you are at one with all the code)
I tend to use -checkaction=context together with -unitest...
That too. Not massively in love with it for (say) approximately equal testing floats but it's there.
mir.test's `shouldApprox` [1] handles that https://github.com/libmir/mir-algorithm/blob/081418a5bb9f22861fc15cbf44045f7aef5c848f/source/mir/test.d#L17
Apr 06 2023
parent Dmytro Katyukha <firemage.dima gmail.com> writes:
On Friday, 7 April 2023 at 00:58:11 UTC, jmh530 wrote:
 On Tuesday, 4 April 2023 at 18:50:11 UTC, max haughton wrote:
 On Tuesday, 4 April 2023 at 16:01:17 UTC, Adam D Ruppe wrote:
 On Tuesday, 4 April 2023 at 15:45:55 UTC, max haughton wrote:
 They are redundant but they allow some slightly nicer 
 failure messages upon failure (i.e. assert fail is not very 
 helpful unless you are at one with all the code)
I tend to use -checkaction=context together with -unitest...
That too. Not massively in love with it for (say) approximately equal testing floats but it's there.
mir.test's `shouldApprox` [1] handles that https://github.com/libmir/mir-algorithm/blob/081418a5bb9f22861fc15cbf44045f7aef5c848f/source/mir/test.d#L17
That's why we need to have some list of standard assertions in standard library. Also, about `-checkaction=context`, i just tried to enable it for my lib, and get linking errors mentioned in discussion earlier. And one more note, i think, that compiler switches is last place where newcomer will look for better assert output. Personally, i think, that implementing it in library could be much better than, trying to make compiler to show good assert messages. Also, i would like to note, that there could be special (custom) assert functions in complex projects, and it would be nice, if they could look similar to those, defined in standard library. Example of such functions could be mentioned above `shouldApprox`, or as real example from other Python project `assertSLAControl(request, sla_control_code, field_name, value)`. And one more reason, why we need library implementation for assertions - i think assetions could be used in two different cased: - `assert` expression in the app/lib code. - Usually, it could be used to test if program is in correct state in runtime, and in case of failure, it is time to start debugging. - I think, for this case current implementation (especially with `-checkaction=context` switch) is good enough. - The key point for this type of usage of `assert` expression - it have to fail in really rare cases. - tests... And there are following requirements/wishes for assertions used in unittests: - Handle special assertions in more readable and less verbose way. Examples could be: - check if expression throws specified exception (optionally with ability to check exception's message via regex) - check if float number is approximately equal to something - comparing lists/arrays/... with ability to print different elements (it is not compiler job) - comparing strings with pointing to part of string that differes (it is not compiler job) - Custom assertions - framework/project specific assertions should look similar to standard assertions to make code more readable. - More verbose output needed to unittests. - Standard naming scheme for assertions (does not matter what we choose as standard but it have to be standatized) Also, i think, it would be nice to have nicer traceback output in general. Just take a look at pythons tracebacks - it is readable (comared to D tracebacks). Well tested app could have more tests (in terms of lines of code) then business logic in some cases, thus, i think, it would be nice to provide good tools, that will allow to easily conver app/lib with tests and support that test suit for long time.
Apr 07 2023
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 4 April 2023 at 12:11:28 UTC, Quirin Schroll wrote:`
 Instead of
 ```d
 assertThrows!Exception(expression);
 ```
 one could write
 ```d
 try { expression(); assert(0); } catch (Exception) {}
 ```
 but I see that it is a non-trivial pattern and `assertThrows` 
 just documents clearly what is expected. Even this isn’t that 
 much.
It's actually already in Phobos: https://dlang.org/phobos/std_exception.html#.assertThrown
 Those functions can provide better error messages, but to be 
 honest, the D compiler’s unittest failure messages should be 
 improved instead.
They have been, with the -checkaction=context switch mentioned in this thread.
Apr 04 2023
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Thursday, 30 March 2023 at 17:01:26 UTC, Dmytro Katyukha wrote:
 But, this feature is not convenient/usable without standard set 
 of `assert*` methods, that could display human-readable output 
 in case of error (what value expected, and what value we got).
 Because of absense of such expected functionality, there are 
 some set of third-party libraries that implement good and 
 readable assertions. But, most popular assertion libraries 
 depend on 
 [unit-threaded](https://code.dlang.org/packages/unit-threaded) 
 that is large test runner.
You can depend on only the assertions dub subpackage, which is tiny, and then use the standard runner from druntime.
 And it is strange, when you try to add small and simple library 
 to dependencies of your project, but it brings whole 
 unit-threaded package to your project.
That's a dub bug. If a project lists a dependency only in the unittest configuration, it adds it to the transitive list of dependencies, which is obviously nonsense.
 And i think, in significant amount of cases, this dependency is 
 added just because standard set of asserts absent in standard 
 library.
And running in threads, and running specific tests, and...
 I think, for small libraries there is no sense to depend on 
 large test runner. Ususally it is enough to use standard test 
 runner.
To each their own, I'd use a "proper" library every time because of the developer experience.
 Just take a look at python's assertions from standard library 
 [unittest](https://docs.python.org/3/library/unittest.html#assert-methods)
None of those are needed if using pytest, which I'd recommend anyone writing Python code to do. One uses the standard Python `assert` and it prints error messages that make sense. It's magic, and it's magic I'd want for D.
 I think, implementation of this list of asserts in the Phobos 
 will allow users to start use unittest much easier.
Nearly every test assertion is of equality. It's nice to have the others when needed, but they're not a good 99.9% of the time.
 Also, the question to authors of assert libraries (like 
 [unit-threaded:assertions](https://code.dlang.org/packages/unit-thr
aded%3Aassertions), [dshould](https://code.dlang.org/packages/dshould), etc):
what do you think about contributing assertions to Phobos?
It's definitely an idea.
Apr 07 2023