Writing own asserts

Standard assertions are enough for most cases, but sometimes for specific cases you may need to write your own assertion to encapsulate the validation logic.

When writing assertions, the most important thing is that the tester can trace all the way from the beginning of the assertion in the test, to the actual call to t.fail() inside your assertion. Without this, if there are two assertions, the fall of one will be indistinguishable from the fall of the other.

To avoid this, each function called on the path to t.fail() must have the #[test_helper] attribute, then the compiler will automatically forward the necessary data to t.fail().

Let's look at a simple example:

struct Expectation[T: Equality] { t &mut testing.Tester val T } #[test_helper] fn (e &mut Expectation[T]) to_be(expected T) { e.t.on_assert() if e.val != expected { e.t.fail("expected value is not equal to actual") } } fn expect[T: Equality](t &mut testing.Tester, val T) -> Expectation[T] { return Expectation t: t, val: val } }

There are two main points to consider in this example:

  1. The to_be function is marked with the #[test_helper] attribute, which allows the compiler to forward data to t.fail().
  2. The to_be function calls the on_assert method on the t object to inform the tester that an assertion has been called. This will allow the tester to display at the end of testing the number of assertions that were called.

Using this assertion in a test would look like this:

test 'custom assert' { expect(t, 1).to_be(2) }