Skip to content

Add monad assertions to the test project - #834

Open
Jack-Edwards wants to merge 2 commits into
stablefrom
test/monad-assertions
Open

Add monad assertions to the test project#834
Jack-Edwards wants to merge 2 commits into
stablefrom
test/monad-assertions

Conversation

@Jack-Edwards

@Jack-Edwards Jack-Edwards commented Jul 31, 2026

Copy link
Copy Markdown
Member

Adds Assert.IsSome, IsNone, IsRight, IsLeft, and IsNeither for Maybe and Either, each with an overload taking an expected value and an Async variant taking the task directly. The ones that match a value return it, so a test can assert the state and bind the value in one statement, and the failure messages name the actual state and its payload.

Assert here is a subclass of NUnit's, declared in the Crypter.Test namespace. Unqualified Assert in a test now resolves to it and reaches the inherited assertions by the same name. No existing assertions were changed.

The assertions that return a value throw on the wrong state rather than calling Fail. Inside an Assert.Multiple scope Fail records the failure and returns, which would leave them handing back a default value for the test to bind and dereference. IsNone and IsNeither call Fail, so a multiple scope still collects them.

🤖 Generated with Claude Code

Jack-Edwards and others added 2 commits July 31, 2026 14:24
Asserting on Maybe and Either meant `Assert.That(result.IsRight, Is.True)`,
which reports only `Expected: True, But was: False` and reveals nothing about
the state the monad was actually in. Reaching the value then took a second step
through a lambda or an *OrDefault call. The new assertions report the actual
state and its payload on failure, and return the matched value so a test can
assert and bind in one statement.

NUnit's Assert cannot be extended, so these live on a subclass of it declared in
the Crypter.Test namespace. Unqualified `Assert` in a test resolves there and
reaches the inherited assertions through the same name as before, which is why
no existing test changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Inside an Assert.Multiple scope, Fail records the failure and returns rather
than throwing. The assertions that return a value went on to hand back a
default, so a test would bind null and report a NullReferenceException in place
of the failure message, and the remaining assertions in the scope never ran.
There is no value to return once the state is wrong, so these now throw.

IsNone and IsNeither still call Fail, so a multiple scope collects them.

Adds coverage for the value-returning assertions inside a multiple scope, and
for the Neither state appearing in an IsRight or IsLeft failure message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant