Summary
Add a .because(reason) method to all expectation types that allows users to provide custom context for assertion failures.
Motivation
Currently, when an assertion fails, the error message shows the expected vs actual values but lacks additional context about why this assertion matters. Users must rely on spec descriptions for context:
it("should have non-empty cart", () => {
expect(count).toBeGreaterThan(0); // No inline context
});
With .because(), users can add context directly to assertions:
expect(count).toBeGreaterThan(0).because("cart should not be empty");
// Error: Expected count to be greater than 0, because cart should not be empty, but was 0
Proposed API
// Works with any expectation type
expect(value).toBe(5).because("user preferences require this value");
expect(items).toContain("apple").because("default fruit must be present");
expect(flag).toBeTrue().because("feature flag should be enabled in test env");
expect(() => action()).toThrow<InvalidOperationException>().because("invalid state should throw");
Implementation Notes
Each expectation struct needs:
- A
_reason field to store the context
- A
because(string reason) method that returns a new instance with the reason set
- Updated error messages to include the reason when present
Example for Expectation<T>:
public readonly struct Expectation<T>
{
private readonly string? _reason;
public Expectation<T> because(string reason)
=> new(Actual, Expression, _isNegated, reason);
public void toBe(T expected)
{
// ... existing logic ...
var reasonClause = _reason != null ? $", because {_reason}" : "";
throw new AssertionException(
$"Expected {Expression} to be {expected}{reasonClause}, but was {Actual}");
}
}
Files to Modify
src/DraftSpec/Expectations/Expectation.cs
src/DraftSpec/Expectations/BoolExpectation.cs
src/DraftSpec/Expectations/StringExpectation.cs
src/DraftSpec/Expectations/CollectionExpectation.cs
src/DraftSpec/Expectations/ActionExpectation.cs
src/DraftSpec/Expectations/AsyncActionExpectation.cs
Background
This feature is inspired by TUnit's .Because() method. See docs/research/tunit-comparison.md for the full analysis.
Acceptance Criteria
Summary
Add a
.because(reason)method to all expectation types that allows users to provide custom context for assertion failures.Motivation
Currently, when an assertion fails, the error message shows the expected vs actual values but lacks additional context about why this assertion matters. Users must rely on spec descriptions for context:
With
.because(), users can add context directly to assertions:Proposed API
Implementation Notes
Each expectation struct needs:
_reasonfield to store the contextbecause(string reason)method that returns a new instance with the reason setExample for
Expectation<T>:Files to Modify
src/DraftSpec/Expectations/Expectation.cssrc/DraftSpec/Expectations/BoolExpectation.cssrc/DraftSpec/Expectations/StringExpectation.cssrc/DraftSpec/Expectations/CollectionExpectation.cssrc/DraftSpec/Expectations/ActionExpectation.cssrc/DraftSpec/Expectations/AsyncActionExpectation.csBackground
This feature is inspired by TUnit's
.Because()method. Seedocs/research/tunit-comparison.mdfor the full analysis.Acceptance Criteria
.because(reason).notnegation:expect(x).not.toBe(5).because("reason")