Skip to content

Commit b706d48

Browse files
committed
Build on top of thomhurst#4940
Seek to address thomhurst#4870
1 parent 833f5e4 commit b706d48

10 files changed

Lines changed: 1019 additions & 190 deletions
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
namespace TUnit.Assertions.Tests;
2+
3+
public class NumericEqualityToleranceAssertionTests
4+
{
5+
[Test]
6+
public async Task Double_RelativeTolerance_Passes_When_Within_Percentage()
7+
{
8+
const double actual = 104.9;
9+
const double expected = 100.0;
10+
11+
await Assert.That(actual)
12+
.IsEqualTo(expected)
13+
.WithinRelativeTolerance(5);
14+
}
15+
16+
[Test]
17+
public async Task Double_RelativeTolerance_Fails_When_Outside_Percentage()
18+
{
19+
const double actual = 105.1;
20+
const double expected = 100.0;
21+
22+
var sut = async () => await Assert.That(actual)
23+
.IsEqualTo(expected)
24+
.WithinRelativeTolerance(5);
25+
26+
await Assert.That(sut).ThrowsException();
27+
}
28+
29+
[Test]
30+
public async Task Double_RelativeTolerance_Includes_Exact_Boundary()
31+
{
32+
const double actual = 105.0;
33+
const double expected = 100.0;
34+
35+
await Assert.That(actual)
36+
.IsEqualTo(expected)
37+
.WithinRelativeTolerance(5);
38+
}
39+
40+
[Test]
41+
public async Task Double_RelativeTolerance_Works_With_Negative_Expected_Value()
42+
{
43+
const double actual = -104.9;
44+
const double expected = -100.0;
45+
46+
await Assert.That(actual)
47+
.IsEqualTo(expected)
48+
.WithinRelativeTolerance(5);
49+
}
50+
51+
[Test]
52+
public async Task Double_RelativeTolerance_Fails_With_Negative_Expected_Value_When_Outside_Boundary()
53+
{
54+
const double actual = -106.0;
55+
const double expected = -100.0;
56+
57+
var sut = async () => await Assert.That(actual)
58+
.IsEqualTo(expected)
59+
.WithinRelativeTolerance(5);
60+
61+
await Assert.That(sut).ThrowsException();
62+
}
63+
64+
[Test]
65+
public async Task Double_RelativeTolerance_ZeroExpected_Passes_Only_For_Exact_Zero()
66+
{
67+
const double actual = 0.0;
68+
const double expected = 0.0;
69+
70+
await Assert.That(actual)
71+
.IsEqualTo(expected)
72+
.WithinRelativeTolerance(5);
73+
}
74+
75+
[Test]
76+
public async Task Double_RelativeTolerance_ZeroExpected_Fails_For_NonZero_Actual()
77+
{
78+
const double actual = 0.0001;
79+
const double expected = 0.0;
80+
81+
var sut = async () => await Assert.That(actual)
82+
.IsEqualTo(expected)
83+
.WithinRelativeTolerance(5);
84+
85+
await Assert.That(sut).ThrowsException();
86+
}
87+
88+
[Test]
89+
public async Task Double_RelativeTolerance_Treats_NaN_As_Equal_To_NaN()
90+
{
91+
const double actual = double.NaN;
92+
const double expected = double.NaN;
93+
94+
await Assert.That(actual)
95+
.IsEqualTo(expected)
96+
.WithinRelativeTolerance(5);
97+
}
98+
99+
[Test]
100+
public async Task Double_RelativeTolerance_Fails_When_Only_One_Side_Is_NaN()
101+
{
102+
const double actual = double.NaN;
103+
const double expected = 100.0;
104+
105+
var sut = async () => await Assert.That(actual)
106+
.IsEqualTo(expected)
107+
.WithinRelativeTolerance(5);
108+
109+
await Assert.That(sut).ThrowsException();
110+
}
111+
112+
[Test]
113+
public async Task Double_RelativeTolerance_Treats_Matching_Positive_Infinity_As_Equal()
114+
{
115+
const double actual = double.PositiveInfinity;
116+
const double expected = double.PositiveInfinity;
117+
118+
await Assert.That(actual)
119+
.IsEqualTo(expected)
120+
.WithinRelativeTolerance(5);
121+
}
122+
123+
[Test]
124+
public async Task Double_RelativeTolerance_Fails_For_Mismatched_Infinities()
125+
{
126+
const double actual = double.PositiveInfinity;
127+
const double expected = double.NegativeInfinity;
128+
129+
var sut = async () => await Assert.That(actual)
130+
.IsEqualTo(expected)
131+
.WithinRelativeTolerance(5);
132+
133+
await Assert.That(sut).ThrowsException();
134+
}
135+
136+
[Test]
137+
public async Task Float_RelativeTolerance_Passes_When_Within_Percentage()
138+
{
139+
const float actual = 104.9f;
140+
const float expected = 100f;
141+
142+
await Assert.That(actual)
143+
.IsEqualTo(expected)
144+
.WithinRelativeTolerance(5);
145+
}
146+
147+
[Test]
148+
public async Task Float_RelativeTolerance_Treats_NaN_As_Equal_To_NaN()
149+
{
150+
const float actual = float.NaN;
151+
const float expected = float.NaN;
152+
153+
await Assert.That(actual)
154+
.IsEqualTo(expected)
155+
.WithinRelativeTolerance(5);
156+
}
157+
158+
[Test]
159+
public async Task Float_RelativeTolerance_Treats_Matching_Infinity_As_Equal()
160+
{
161+
const float actual = float.PositiveInfinity;
162+
const float expected = float.PositiveInfinity;
163+
164+
await Assert.That(actual)
165+
.IsEqualTo(expected)
166+
.WithinRelativeTolerance(5);
167+
}
168+
169+
[Test]
170+
public async Task Decimal_RelativeTolerance_Passes_When_Within_Percentage()
171+
{
172+
const decimal actual = 104.9m;
173+
const decimal expected = 100m;
174+
175+
await Assert.That(actual)
176+
.IsEqualTo(expected)
177+
.WithinRelativeTolerance(5);
178+
}
179+
180+
[Test]
181+
public async Task Decimal_RelativeTolerance_Fails_When_Outside_Percentage()
182+
{
183+
const decimal actual = 105.1m;
184+
const decimal expected = 100m;
185+
186+
var sut = async () => await Assert.That(actual)
187+
.IsEqualTo(expected)
188+
.WithinRelativeTolerance(5);
189+
190+
await Assert.That(sut).ThrowsException();
191+
}
192+
193+
[Test]
194+
public async Task RelativeTolerance_Exception_Message_Contains_Expectation_Text()
195+
{
196+
const double actual = 106.0;
197+
const double expected = 100.0;
198+
199+
var sut = async () => await Assert.That(actual)
200+
.IsEqualTo(expected)
201+
.WithinRelativeTolerance(5);
202+
203+
var exception = await Assert.That(sut).ThrowsException();
204+
205+
await Assert.That(exception.Message.NormalizeLineEndings())
206+
.Contains("Expected to be within 5% of 100")
207+
.And.Contains("but found 106")
208+
.And.Contains("Assert.That(actual).IsEqualTo(expected).WithinRelativeTolerance(5)");
209+
}
210+
211+
[Test]
212+
public async Task RelativeTolerance_Exception_Message_Contains_Difference_Details()
213+
{
214+
const double actual = 106.0;
215+
const double expected = 100.0;
216+
217+
var sut = async () => await Assert.That(actual)
218+
.IsEqualTo(expected)
219+
.WithinRelativeTolerance(5);
220+
221+
var exception = await Assert.That(sut).ThrowsException();
222+
223+
await Assert.That(exception.Message.NormalizeLineEndings())
224+
.Contains("differs by 6")
225+
.And.Contains("relative tolerance of 5%");
226+
}
227+
}

TUnit.Assertions/Conditions/NumericIsCloseToAssertion.cs

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,9 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<double> m
5757

5858
var diff = Math.Abs(value - _expected);
5959

60-
if (diff <= _tolerance)
61-
{
62-
return AssertionResult._passedTask;
63-
}
64-
65-
return Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
60+
return diff <= _tolerance
61+
? AssertionResult._passedTask
62+
: Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
6663
}
6764

6865
protected override string GetExpectation() =>
@@ -167,12 +164,9 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<int> meta
167164

168165
var diff = Math.Abs((long)value - _expected);
169166

170-
if (diff <= _tolerance)
171-
{
172-
return AssertionResult._passedTask;
173-
}
174-
175-
return Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
167+
return diff <= _tolerance
168+
? AssertionResult._passedTask
169+
: Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
176170
}
177171

178172
protected override string GetExpectation() =>
@@ -209,14 +203,11 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<long> met
209203
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}"));
210204
}
211205

212-
var diff = Math.Abs((double)value - _expected);
206+
var diff = Math.Abs((double) value - (double) _expected);
213207

214-
if (diff <= (double)_tolerance)
215-
{
216-
return AssertionResult._passedTask;
217-
}
218-
219-
return Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
208+
return diff <= (double) _tolerance
209+
? AssertionResult._passedTask
210+
: Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
220211
}
221212

222213
protected override string GetExpectation() =>
@@ -255,12 +246,9 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<decimal>
255246

256247
var diff = Math.Abs(value - _expected);
257248

258-
if (diff <= _tolerance)
259-
{
260-
return AssertionResult._passedTask;
261-
}
262-
263-
return Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
249+
return diff <= _tolerance
250+
? AssertionResult._passedTask
251+
: Task.FromResult(AssertionResult.Failed($"found {value}, which differs by {diff}"));
264252
}
265253

266254
protected override string GetExpectation() =>

TUnit.Assertions/Conditions/NumericIsWithinPercentOfAssertion.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<double> m
6363
return AssertionResult._passedTask;
6464
}
6565

66+
var actualPercent = _expected != 0 ? (diff / Math.Abs(_expected)) * 100 : double.PositiveInfinity;
67+
6668
return Task.FromResult(AssertionResult.Failed(
67-
$"found {value}, which differs by {diff} ({(diff / Math.Abs(_expected)) * 100:F2}% of expected)"));
69+
$"found {value}, which differs by {diff} ({actualPercent:F2}% of expected)"));
70+
6871
}
6972

7073
protected override string GetExpectation() =>
@@ -126,12 +129,9 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<float> me
126129
var diff = Math.Abs(value - _expected);
127130
var allowedDelta = Math.Abs(_expected * _percent / 100.0f);
128131

129-
if (diff <= allowedDelta)
130-
{
131-
return AssertionResult._passedTask;
132-
}
133-
134-
return Task.FromResult(AssertionResult.Failed(
132+
return diff <= allowedDelta
133+
? AssertionResult._passedTask
134+
: Task.FromResult(AssertionResult.Failed(
135135
$"found {value}, which differs by {diff} ({(diff / Math.Abs(_expected)) * 100:F2}% of expected)"));
136136
}
137137

@@ -178,6 +178,7 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<int> meta
178178
}
179179

180180
var actualPercent = _expected != 0 ? (diff / Math.Abs(_expected)) * 100 : double.PositiveInfinity;
181+
181182
return Task.FromResult(AssertionResult.Failed(
182183
$"found {value}, which differs by {(long)diff} ({actualPercent:F2}% of expected)"));
183184
}
@@ -225,6 +226,7 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<long> met
225226
}
226227

227228
var actualPercent = _expected != 0 ? (diff / Math.Abs(_expected)) * 100 : double.PositiveInfinity;
229+
228230
return Task.FromResult(AssertionResult.Failed(
229231
$"found {value}, which differs by {(long)diff} ({actualPercent:F2}% of expected)"));
230232
}
@@ -273,6 +275,7 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<decimal>
273275

274276
var actualPercent = _expected != 0 ? (diff / Math.Abs(_expected)) * 100m : -1m;
275277
var percentDisplay = actualPercent >= 0 ? $"{actualPercent:F2}%" : "Infinity%";
278+
276279
return Task.FromResult(AssertionResult.Failed(
277280
$"found {value}, which differs by {diff} ({percentDisplay} of expected)"));
278281
}

0 commit comments

Comments
 (0)