Skip to content

feat: support .Within tolerance on numeric comparison expectations#943

Merged
vbreuss merged 4 commits into
mainfrom
feat/within-numeric-comparisons
May 10, 2026
Merged

feat: support .Within tolerance on numeric comparison expectations#943
vbreuss merged 4 commits into
mainfrom
feat/within-numeric-comparisons

Conversation

@vbreuss
Copy link
Copy Markdown
Member

@vbreuss vbreuss commented May 10, 2026

Extend the existing .Within(tolerance) mechanism — previously only available on IsEqualTo / IsNotEqualTo / IsOneOf — to ThatNumber's comparison and range methods: IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsLessThanOrEqualTo, IsBetween, and IsNotBetween.

When .Within(t) is set, strict comparisons become inclusive at the new boundary (e.g. IsGreaterThan(5).Within(1) accepts actual >= 4); without .Within, the original strict behavior is preserved.


@vbreuss vbreuss self-assigned this May 10, 2026
@vbreuss vbreuss added the enhancement New feature or request label May 10, 2026
Copilot AI review requested due to automatic review settings May 10, 2026 12:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends aweXpect’s numeric .Within(tolerance) option (previously available on equality/one-of expectations) to comparison and range expectations, enabling tolerant comparisons like IsGreaterThan(5).Within(1).

Changes:

  • Add tolerance-aware result types (NumberToleranceResult / NullableNumberToleranceResult) to IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsLessThanOrEqualTo, IsBetween, and IsNotBetween.
  • Implement tolerance-aware comparison/range logic via new helpers on NumberTolerance<TNumber>.
  • Add/extend unit tests for the new .Within(...) behavior across several numeric types.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
Tests/aweXpect.Tests/Numbers/ThatNumber.IsLessThanOrEqualTo.WithinTests.cs New tests for .Within(...) on IsLessThanOrEqualTo.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsLessThanOrEqualTo.Tests.cs Make nested test class partial to allow additional test files.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsLessThan.WithinTests.cs New tests for .Within(...) on IsLessThan (incl. strict vs tolerant behavior).
Tests/aweXpect.Tests/Numbers/ThatNumber.IsLessThan.Tests.cs Make nested test class partial to allow additional test files.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsGreaterThanOrEqualTo.WithinTests.cs New tests for .Within(...) on IsGreaterThanOrEqualTo.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsGreaterThanOrEqualTo.Tests.cs Make nested test class partial to allow additional test files.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsGreaterThan.WithinTests.cs New tests for .Within(...) on IsGreaterThan (incl. strict vs tolerant behavior).
Tests/aweXpect.Tests/Numbers/ThatNumber.IsGreaterThan.Tests.cs Make nested test class partial to allow additional test files.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsBetween.WithinTests.cs New tests for widening range semantics with .Within(...) on IsBetween/IsNotBetween.
Tests/aweXpect.Tests/Numbers/ThatNumber.IsBetween.Tests.cs Make nested test class partial to allow additional test files.
Source/aweXpect/That/Numbers/ThatNumber.IsLessThanOrEqualTo.cs Change return types to tolerance-capable results and route evaluation through NumberTolerance.
Source/aweXpect/That/Numbers/ThatNumber.IsLessThan.cs Change return types to tolerance-capable results and route evaluation through NumberTolerance.
Source/aweXpect/That/Numbers/ThatNumber.IsGreaterThanOrEqualTo.cs Change return types to tolerance-capable results and route evaluation through NumberTolerance.
Source/aweXpect/That/Numbers/ThatNumber.IsGreaterThan.cs Change return types to tolerance-capable results and route evaluation through NumberTolerance.
Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Make IsBetween/IsNotBetween produce tolerance-capable results and apply tolerance to range bounds.
Source/aweXpect.Core/Options/NumberTolerance.cs Add tolerance-aware comparison and range helper methods.

Comment thread Source/aweXpect.Core/Options/NumberTolerance.cs Outdated
Comment thread Source/aweXpect.Core/Options/NumberTolerance.cs Outdated
Comment thread Source/aweXpect.Core/Options/NumberTolerance.cs Outdated
Comment thread Source/aweXpect.Core/Options/NumberTolerance.cs Outdated
Comment thread Tests/aweXpect.Tests/Numbers/ThatNumber.IsBetween.WithinTests.cs
Comment thread Tests/aweXpect.Tests/Numbers/ThatNumber.IsBetween.WithinTests.cs
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

Test Results

     23 files   -  27       23 suites   - 27   7m 47s ⏱️ +29s
 19 762 tests +203   19 761 ✅ +203  1 💤 ±0  0 ❌ ±0 
102 122 runs  +963  102 121 ✅ +963  1 💤 ±0  0 ❌ ±0 

Results for commit 061a167. ± Comparison against base commit 6c19073.

This pull request removes 3102 and adds 3305 tests. Note that renamed tests count towards both.
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message3175baa5-84b4-48d6-8a6e-1921383e84a4")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message738fb174-6531-4278-821a-1a5182440cf3")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message7a4e5097-3d16-4432-9666-4ac37d448ac8")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messaged2b1b247-6858-484c-a499-1d3a2badb8fd")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messageeb5338f6-1509-48fb-a0be-e705981680fa")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagefed07d10-e679-408e-9578-db95ba8c23a7")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message0451b572-7977-45b1-a845-5c860a5691ca")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message52b0ecc9-974f-47a5-97dd-3bfad163ce1c")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message84a2a7da-7841-4da7-9b30-5fbdca9e32d9")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message9d52e3aa-2ef4-4403-9fa2-3cc3ceba95a2")
…
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message12f213ed-5cc7-44d0-aa76-a2f798b71918")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message3560d027-0653-44de-be47-a71bc90bac9d")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message4f587f14-5f43-43e6-9f2f-d3b46e917095")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message6094ccd2-2208-4475-99b3-3cde278a782a")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message99a52040-1f42-4631-bab4-b9e6199978f1")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messageaae930a9-80f2-4122-99cf-31b5174dda28")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message4bc80c8b-b107-4836-b310-e8e305990737")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message611ab89a-1f12-44ae-b8ef-e00f7cf98b2f")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message6475d8ff-7c9a-470c-9d89-8ef63f5dc959")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message9d8a7056-9e08-41e1-a0d1-f44ce11fb536")
…

♻️ This comment has been updated with latest results.

vbreuss added 2 commits May 10, 2026 16:10
Extend the existing .Within(tolerance) mechanism — previously only available
on IsEqualTo / IsNotEqualTo / IsOneOf — to ThatNumber's comparison and range
methods: IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan, IsLessThanOrEqualTo,
IsBetween, and IsNotBetween.

When .Within(t) is set, strict comparisons become inclusive at the new
boundary (e.g. IsGreaterThan(5).Within(1) accepts actual >= 4); without
.Within, the original strict behavior is preserved.

Closes #894
Guards `NumberTolerance` comparison helpers against `OverflowException`
from checked difference lambdas (e.g. `sbyte.MinValue` vs `sbyte.MaxValue`)
via a shared `TryCalculateDifference` helper, aligns the netstandard2.0
nullable `IsBetween` constraint with the NET8+ variant by inheriting from
`WithEqualToValue`, and expands `Within` test coverage (nullable overloads,
expected-NaN, overflow-safe sbyte, OrEqualTo parity, float/long failure
paths).
Copilot AI review requested due to automatic review settings May 10, 2026 14:11
@vbreuss vbreuss force-pushed the feat/within-numeric-comparisons branch from 80d3ac0 to 2d04373 Compare May 10, 2026 14:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.

Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Outdated
Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Outdated
Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Outdated
Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Outdated
Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsBetween.cs Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

🚀 Benchmark Results

Details

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.80GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.203
[Host] : .NET 8.0.26 (8.0.26, 8.0.2626.16921), X64 RyuJIT x86-64-v3

Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=1 WarmupCount=10

Method Mean Error StdDev Gen0 Gen1 Allocated
Bool_aweXpect 248.5 ns 2.30 ns 2.04 ns 0.0415 - 696 B
Bool_FluentAssertions 231.2 ns 2.22 ns 1.85 ns 0.0567 - 952 B
Equivalency_aweXpect 294,356.2 ns 1,373.69 ns 1,284.95 ns 20.0195 0.4883 335444 B
Equivalency_FluentAssertions 2,610,953.7 ns 9,226.48 ns 7,704.53 ns 285.1563 46.8750 4804906 B
Int_GreaterThan_aweXpect 263.0 ns 0.83 ns 0.73 ns 0.0515 - 864 B
Int_GreaterThan_FluentAssertions 233.3 ns 0.83 ns 0.77 ns 0.0730 - 1224 B
ItemsCount_AtLeast_aweXpect 464.1 ns 0.89 ns 0.75 ns 0.0811 - 1360 B
ItemsCount_AtLeast_FluentAssertions 462.1 ns 2.14 ns 2.00 ns 0.1197 - 2008 B
String_aweXpect 452.6 ns 1.83 ns 1.62 ns 0.0672 - 1128 B
String_FluentAssertions 1,241.8 ns 8.78 ns 7.33 ns 0.2346 - 3944 B
StringArray_aweXpect 1,886.2 ns 16.94 ns 15.84 ns 0.1564 - 2624 B
StringArray_FluentAssertions 1,296.5 ns 25.86 ns 24.19 ns 0.2480 - 4152 B
StringArrayInAnyOrder_aweXpect 2,532.1 ns 18.24 ns 17.06 ns 0.1678 - 2816 B
StringArrayInAnyOrder_FluentAssertions 92,535.9 ns 400.16 ns 354.73 ns 3.4180 - 57957 B

@github-actions
Copy link
Copy Markdown
Contributor

👽 Mutation Results

Mutation testing badge

aweXpect

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Total Detected Total Undetected Total Mutants
Helpers/NumberToleranceExtensions.cs 92.11% 70 2 0 4 17 0 70 6 93
That/Numbers/ThatNumber.IsBetween.cs 92.45% 49 4 0 0 12 16 49 4 81
That/Numbers/ThatNumber.IsGreaterThan.cs 83.33% 25 5 0 0 8 12 25 5 50
That/Numbers/ThatNumber.IsGreaterThanOrEqualTo.cs 83.33% 25 5 0 0 8 12 25 5 50
That/Numbers/ThatNumber.IsLessThan.cs 83.33% 25 5 0 0 8 12 25 5 50
That/Numbers/ThatNumber.IsLessThanOrEqualTo.cs 83.33% 25 5 0 0 8 12 25 5 50

The final mutation score is 87.95%

Coverage Thresholds: high:80 low:60 break:0

aweXpect.Core

Details
File Score Killed Survived Timeout No Coverage Ignored Compile Errors Total Detected Total Undetected Total Mutants

The final mutation score is NaN%

Coverage Thresholds: high:80 low:60 break:0

Copilot AI review requested due to automatic review settings May 10, 2026 14:53
@vbreuss vbreuss enabled auto-merge (squash) May 10, 2026 14:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comment thread Source/aweXpect/That/Numbers/ThatNumber.IsGreaterThan.cs
@vbreuss vbreuss changed the title feat: support .Within tolerance on numeric comparison expectations feat: support .Within tolerance on numeric comparison expectations May 10, 2026
@vbreuss vbreuss force-pushed the feat/within-numeric-comparisons branch from 0828fd3 to 061a167 Compare May 10, 2026 15:09
@sonarqubecloud
Copy link
Copy Markdown

@vbreuss vbreuss merged commit 8cee919 into main May 10, 2026
13 checks passed
@vbreuss vbreuss deleted the feat/within-numeric-comparisons branch May 10, 2026 15:18
github-actions Bot added a commit that referenced this pull request May 10, 2026
…ric comparison expectations (#943) by Valentin Breuß
github-actions Bot added a commit that referenced this pull request May 10, 2026
…ric comparison expectations (#943) by Valentin Breuß
@github-actions
Copy link
Copy Markdown
Contributor

This is addressed in release v2.32.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request state: released The issue is released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add .Within tolerance also for other numeric expectations

2 participants