Skip to content

feat: support AssertionResult<T> in GenerateAssertion for terminal assertions#4843

Merged
thomhurst merged 3 commits intomainfrom
feat/generate-assertion-result-of-t
Feb 18, 2026
Merged

feat: support AssertionResult<T> in GenerateAssertion for terminal assertions#4843
thomhurst merged 3 commits intomainfrom
feat/generate-assertion-result-of-t

Conversation

@thomhurst
Copy link
Owner

Summary

  • Introduces AssertionResult<T> readonly struct for assertions that return a value when awaited (terminal assertions)
  • Extends [GenerateAssertion] source generator to recognize AssertionResult<T> and Task<AssertionResult<T>> return types
  • Generated assertion classes include _result capture field, new GetAwaiter() override, and ExecuteAndReturnResultAsync() to enable string matched = await Assert.That(items).ContainsMatch("foo")

Closes #4829

Usage

[GenerateAssertion(ExpectationMessage = "to contain '{needle}'")]
public static AssertionResult<string> ContainsMatch(this IEnumerable<string> strings, string needle)
{
    var result = strings.FirstOrDefault(x => x.Contains(needle));
    if (result is not null)
        return AssertionResult<string>.Passed(result);
    return AssertionResult.Failed($"{needle} not found");
}

// Terminal assertion — returns the matched item:
string matched = await Assert.That(items).ContainsMatch("foo");

Test plan

  • Source generator snapshot tests for sync AssertionResult<T> return type
  • Source generator snapshot tests for async Task<AssertionResult<T>> return type
  • Verified generated code includes _result field, new GetAwaiter<T>(), and unwrapping logic
  • Existing MethodAssertionGeneratorTests pass without regression
  • TUnit.Assertions builds cleanly across all target frameworks (net8.0, net9.0, net10.0, netstandard2.0)

🤖 Generated with Claude Code

…sertions

Adds support for [GenerateAssertion] methods that return AssertionResult<T>
or Task<AssertionResult<T>>, enabling generated assertions that return a value
when awaited (terminal assertions). Previously this required hand-writing
the entire assertion class.

Closes #4829

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Feb 18, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

This PR introduces a well-designed feature for terminal assertions that return typed values. The implementation:

  • Properly handles both sync (AssertionResult<T>) and async (Task<AssertionResult<T>>) patterns
  • Uses readonly struct for zero-allocation value semantics
  • Correctly committed snapshot test files (only .verified.txt)
  • Follows existing TUnit patterns consistently
  • Maintains type safety with appropriate nullable handling

- Regenerate all .verified.txt files to match current generator output
- Fix RefStructParameter test: add PreprocessorSymbols support to
  RunTestOptions and pass NET6_0_OR_GREATER when testing ref struct
  parameter handling
- Simplify ContainsMessage test data to use expression body (required
  for InlineMethodBody with ref struct parameters)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The assertions public API tests were failing because the new
AssertionResult<T> type was added but the verified snapshot files
were not updated to reflect this API addition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thomhurst thomhurst enabled auto-merge (squash) February 18, 2026 19:58
@thomhurst thomhurst merged commit 820db3f into main Feb 18, 2026
14 checks passed
@thomhurst thomhurst deleted the feat/generate-assertion-result-of-t branch February 18, 2026 20:04
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.

[Feature]: Support custom results in GenerateAssertion

1 participant

Comments