Skip to content

Releases: yevhen/sharp.assert

v1.4.1

Choose a tag to compare

@yevhen yevhen released this 28 Jan 20:06

Added new logo

v1.4.0

Choose a tag to compare

@yevhen yevhen released this 18 Dec 17:15

New Features

Collection Quantifiers

A complete suite of collection quantifier expectations for testing element conditions:

using SharpAssert;

// All items must match
Assert(items.Each(x => x > 0));
Assert(items.Each(x => x.IsEquivalentTo(template)));

// At least one must match  
Assert(items.Some(x => x.Matches("error*")));

// No items may match
Assert(items.None(x => x == null));

// Exactly one must match
Assert(items.One(x => x.Id == targetId));

// Count-based quantifiers
Assert(items.Exactly(3, x => x.Score > 90));
Assert(items.AtLeast(2, x => x.IsEquivalentTo(admin)));
Assert(items.AtMost(1, x => x.Matches("*error*")));

All quantifiers support both predicate expressions (x => x > 5) and custom expectations (x => x.IsEquivalentTo(expected)).

Lambda-Based Custom Expectations

New Expectation.From() factory eliminates boilerplate when creating custom expectations:

public static Expectation IsEven(this int value) =>
    Expectation.From(
        () => value % 2 == 0,
        () => [$"Expected even number, got {value}"]
    );

Assert(4.IsEven());

Multiple Assertions with &&

Logical && now evaluates ALL operands and shows all failures - no short-circuit:

Assert(x == 5 && y == 10);
// Shows both failures when both operands are false:
// &&: Both operands were false

Replaces verbose Assert.Multiple() / AssertionScope patterns with native C# syntax.

Full Changelog: v1.3.0...v1.4.0

v1.3.0

Choose a tag to compare

@yevhen yevhen released this 16 Dec 15:31

New Features

Collection Assertions

  • ContainsInOrder(seq) - validates elements appear in order (gaps allowed)
  • ContainsInConsecutiveOrder(seq) - validates elements appear consecutively (no gaps)
  • IsSubsetOf(superset) - validates collection is a subset
  • IsSupersetOf(subset) - validates collection is a superset
  • Intersects(other) - validates collections share at least one element
  • Satisfies(p1, p2, ...) - validates elements satisfy predicates with unique 1:1 matching (bipartite matching)

Proximity Assertions

  • BeCloseTo(target, tolerance) - validates numeric values within tolerance
  • BeApproximately(target, tolerance) - alias for BeCloseTo
  • BeCloseTo(expected, TimeSpan) - validates DateTime/DateTimeOffset within tolerance

IsEquivalentTo Enhancements

  • Full feature parity with FluentAssertions configuration options

Usage

using SharpAssert.Features.Collections;
using SharpAssert.Features.Proximity;

// Collection assertions
Assert(coll.ContainsInOrder(1, 3, 5));
Assert(coll.ContainsInConsecutiveOrder(2, 3, 4));
Assert(coll.IsSubsetOf(superset));
Assert(coll.IsSupersetOf(subset));
Assert(coll.Intersects(other));
Assert(users.Satisfies(u => u.Age > 18, u => u.IsAdmin));

// Proximity assertions
Assert(value.BeCloseTo(target, 0.01));
Assert(value.BeApproximately(pi, 0.001));
Assert(dt.BeCloseTo(expected, TimeSpan.FromMilliseconds(100)));

Full Changelog: v1.2.0...v1.3.0

v1.2.0

Choose a tag to compare

@yevhen yevhen released this 15 Dec 07:45

What's New

Custom Expectations

Create reusable, composable expectations by inheriting from Expectation:

sealed class IsEvenExpectation(int value) : Expectation
{
    public override EvaluationResult Evaluate(ExpectationContext context) =>
        value % 2 == 0
            ? ExpectationResults.Pass(context.Expression)
            : ExpectationResults.Fail(context.Expression, $"Expected even, got {value}");
}

// Use with static factories or extension methods
Assert(IsEven(4));
Assert(4.IsEven() & !5.IsEven());

String Pattern Matching

Wildcard patterns with * (any sequence) and ? (single character):

using SharpAssert.Features.Strings;

Assert("hello world".Matches("hello *"));
Assert("test.txt".Matches("*.txt"));
Assert("HELLO".MatchesIgnoringCase("hello"));

Occurrence counting for substrings and regex:

Assert("error, error".Contains("error", Occur.Exactly(2)));
Assert("warn, warn".Contains("warn", Occur.AtLeast(1)));
Assert("test123 test456".MatchesRegex(@"test\d+", Occur.Exactly(2)));

Collection Ordering

Validate collections are sorted:

using SharpAssert.Features.Collections;

Assert(numbers.IsInAscendingOrder());
Assert(scores.IsInDescendingOrder());
Assert(names.IsInAscendingOrder(StringComparer.OrdinalIgnoreCase));

Collection Uniqueness

Validate collections contain no duplicates:

Assert(items.AllUnique());
Assert(users.AllUnique(u => u.Email));  // By key

Object Equivalency

Structural comparison with fluent configuration:

Assert(actual.IsEquivalentTo(expected));
Assert(actual.IsEquivalentTo(expected, c => c.Excluding(p => p.Id)));
Assert(actual.IsEquivalentTo(expected, c => c.Including(p => p.Name)));
Assert(team1.IsEquivalentTo(team2, c => c.WithoutStrictOrdering()));

Improvements

  • Programmatic access to results - SharpAssertionException.Result property provides structured access to assertion evaluation results
  • Hardened expression evaluation - Better fallbacks and sentinels for edge cases in expression compilation
  • Improved expression display - Fixed display for complex nested binary operations and nullable unwrapping

Internal

  • Feature-based code organization
  • Decoupled evaluation from formatting
  • Structured comparison results with rendering pushed into record types
  • Simplified rendering without visitors

Full Changelog: v1.1.0...v1.2.0

Release 1.1.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 21:36
v1.1.0

Tidy up CONSTITUTION.md

Release 1.0.6

Choose a tag to compare

@github-actions github-actions released this 13 Aug 19:22
v1.0.6

Fix(build): Correct MSBuild targets for cross-platform compatibility

Release 1.0.5

Choose a tag to compare

@github-actions github-actions released this 13 Aug 18:21
v1.0.5

Add project brief

Release 1.0.4

Choose a tag to compare

@github-actions github-actions released this 13 Aug 15:31
Fix MSBuild targets to preserve unprocessed source files

The previous implementation removed ALL source files from compilation
and only included rewritten files. This broke namespace resolution for
types in files without Assert() calls (like PipeMock).

This fix:
- Only removes files that were actually rewritten
- Includes both rewritten files AND unprocessed source files
- Preserves the complete compilation context

Fixes namespace resolution errors like:
CS0103: The name 'PipeMock' does not exist in the current context

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

1.0.3

Choose a tag to compare

@yevhen yevhen released this 13 Aug 14:20

First beta release for early adopters to gather feedback

1.0.0

1.0.0 Pre-release
Pre-release

Choose a tag to compare

@yevhen yevhen released this 13 Aug 13:59

Zero value release to test the pipeline