Skip to content

Commit 7f2d08a

Browse files
authored
fix: failure message when using Within (#86)
This PR improves the failure output when Mockolate verifications are used with `Within(...)` by ensuring timeouts produce aweXpect failure messages that include a useful “Interactions” context, and adds regression tests for the expected messages. ### Key Changes: - Catch `MockVerificationTimeoutException` during async verification to convert timeouts into aweXpect constraint failures and attach interactions context. - Add new test cases asserting descriptive failure messages for `Within(...)` across multiple count-based constraints. - Update Mockolate package reference from `2.0.0-pre.7` to `2.0.0`.
1 parent 730514d commit 7f2d08a

13 files changed

Lines changed: 337 additions & 49 deletions

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<PackageVersion Include="aweXpect" Version="2.31.0" />
77
<PackageVersion Include="aweXpect.Core" Version="2.28.0" />
88
<PackageVersion Include="aweXpect.Chronology" Version="1.0.0" />
9-
<PackageVersion Include="Mockolate" Version="2.0.0-pre.7" />
9+
<PackageVersion Include="Mockolate" Version="2.0.0" />
1010
</ItemGroup>
1111
<ItemGroup>
1212
<PackageVersion Include="Nullable" Version="1.3.1" />

Source/aweXpect.Mockolate/ThatMockVerify.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
using System;
2-
using System.Diagnostics.CodeAnalysis;
3-
using System.Text;
4-
using aweXpect.Core;
5-
using aweXpect.Core.Constraints;
6-
using Mockolate.Verify;
1+
using Mockolate.Verify;
72

83
namespace aweXpect;
94

Source/aweXpect.Mockolate/ThatVerificationResult.Between.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using aweXpect.Options;
99
using aweXpect.Results;
1010
using Mockolate;
11+
using Mockolate.Exceptions;
1112
using Mockolate.Verify;
1213

1314
namespace aweXpect;
@@ -67,17 +68,32 @@ public async Task<ConstraintResult> IsMetBy(VerificationResult<TVerify> actual,
6768
{
6869
_expectation = asyncVerificationResult.Expectation;
6970
Actual = actual;
70-
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
71+
try
7172
{
72-
string context = Formatter.Format(interactions, FormattingOptions.MultipleLines);
73-
expectationBuilder.UpdateContexts(contexts => contexts.Add(
74-
new ResultContext.SyncCallback("Interactions", () => context)));
75-
_count = interactions.Length;
76-
return interactions.Length >= minimum && interactions.Length <= maximum;
77-
})
78-
? Outcome.Success
79-
: Outcome.Failure;
80-
return this;
73+
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
74+
{
75+
string interactionsText = Formatter.Format(interactions, FormattingOptions.MultipleLines);
76+
expectationBuilder.UpdateContexts(contexts => contexts
77+
.Remove("Interactions")
78+
.Add(new ResultContext.SyncCallback("Interactions", () => interactionsText)));
79+
_count = interactions.Length;
80+
return interactions.Length >= minimum && interactions.Length <= maximum;
81+
})
82+
? Outcome.Success
83+
: Outcome.Failure;
84+
return this;
85+
}
86+
catch (MockVerificationTimeoutException)
87+
{
88+
string interactionsText = Formatter.Format(((IVerificationResult)actual).MockInteractions.Interactions,
89+
FormattingOptions.MultipleLines);
90+
expectationBuilder.UpdateContexts(contexts => contexts
91+
.Remove("Interactions")
92+
.Add(new ResultContext.SyncCallback("Interactions",
93+
() => interactionsText)));
94+
Outcome = Outcome.Failure;
95+
return this;
96+
}
8197
}
8298

8399
IVerificationResult result = actual;

Source/aweXpect.Mockolate/ThatVerificationResult.Times.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using aweXpect.Options;
1111
using aweXpect.Results;
1212
using Mockolate;
13+
using Mockolate.Exceptions;
1314
using Mockolate.Verify;
1415

1516
namespace aweXpect;
@@ -67,17 +68,32 @@ public async Task<ConstraintResult> IsMetBy(VerificationResult<TVerify> actual,
6768
{
6869
_expectation = asyncVerificationResult.Expectation;
6970
Actual = actual;
70-
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
71+
try
7172
{
72-
string context = Formatter.Format(interactions, FormattingOptions.MultipleLines);
73-
expectationBuilder.UpdateContexts(contexts => contexts.Add(
74-
new ResultContext.SyncCallback("Interactions", () => context)));
75-
_count = interactions.Length;
76-
return predicate(_count);
77-
})
78-
? Outcome.Success
79-
: Outcome.Failure;
80-
return this;
73+
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
74+
{
75+
string interactionsText = Formatter.Format(interactions, FormattingOptions.MultipleLines);
76+
expectationBuilder.UpdateContexts(contexts => contexts
77+
.Remove("Interactions")
78+
.Add(new ResultContext.SyncCallback("Interactions", () => interactionsText)));
79+
_count = interactions.Length;
80+
return predicate(_count);
81+
})
82+
? Outcome.Success
83+
: Outcome.Failure;
84+
return this;
85+
}
86+
catch (MockVerificationTimeoutException)
87+
{
88+
string interactionsText = Formatter.Format(((IVerificationResult)actual).MockInteractions.Interactions,
89+
FormattingOptions.MultipleLines);
90+
expectationBuilder.UpdateContexts(contexts => contexts
91+
.Remove("Interactions")
92+
.Add(new ResultContext.SyncCallback("Interactions",
93+
() => interactionsText)));
94+
Outcome = Outcome.Failure;
95+
return this;
96+
}
8197
}
8298

8399
IVerificationResult result = actual;

Source/aweXpect.Mockolate/ThatVerificationResult.cs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using aweXpect.Helpers;
88
using aweXpect.Options;
99
using Mockolate;
10+
using Mockolate.Exceptions;
1011
using Mockolate.Verify;
1112

1213
namespace aweXpect;
@@ -58,17 +59,32 @@ public async Task<ConstraintResult> IsMetBy(VerificationResult<TVerify> actual,
5859
{
5960
_expectation = asyncVerificationResult.Expectation;
6061
Actual = actual;
61-
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
62+
try
6263
{
63-
string context = Formatter.Format(interactions, FormattingOptions.MultipleLines);
64-
expectationBuilder.UpdateContexts(contexts => contexts.Add(
65-
new ResultContext.SyncCallback("Interactions", () => context)));
66-
_count = interactions.Length;
67-
return interactions.Length == expected;
68-
})
69-
? Outcome.Success
70-
: Outcome.Failure;
71-
return this;
64+
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
65+
{
66+
string interactionsText = Formatter.Format(interactions, FormattingOptions.MultipleLines);
67+
expectationBuilder.UpdateContexts(contexts => contexts
68+
.Remove("Interactions")
69+
.Add(new ResultContext.SyncCallback("Interactions", () => interactionsText)));
70+
_count = interactions.Length;
71+
return interactions.Length == expected;
72+
})
73+
? Outcome.Success
74+
: Outcome.Failure;
75+
return this;
76+
}
77+
catch (MockVerificationTimeoutException)
78+
{
79+
string interactionsText = Formatter.Format(((IVerificationResult)actual).MockInteractions.Interactions,
80+
FormattingOptions.MultipleLines);
81+
expectationBuilder.UpdateContexts(contexts => contexts
82+
.Remove("Interactions")
83+
.Add(new ResultContext.SyncCallback("Interactions",
84+
() => interactionsText)));
85+
Outcome = Outcome.Failure;
86+
return this;
87+
}
7288
}
7389

7490
IVerificationResult result = actual;
@@ -238,17 +254,32 @@ public async Task<ConstraintResult> IsMetBy(VerificationResult<TVerify> actual,
238254
{
239255
_expectation = asyncVerificationResult.Expectation;
240256
Actual = actual;
241-
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
257+
try
258+
{
259+
Outcome = await asyncVerificationResult.VerifyAsync(interactions =>
260+
{
261+
string interactionsText = Formatter.Format(interactions, FormattingOptions.MultipleLines);
262+
expectationBuilder.UpdateContexts(contexts => contexts
263+
.Remove("Interactions")
264+
.Add(new ResultContext.SyncCallback("Interactions", () => interactionsText)));
265+
_count = interactions.Length;
266+
return interactions.Length >= expected;
267+
})
268+
? Outcome.Success
269+
: Outcome.Failure;
270+
return this;
271+
}
272+
catch (MockVerificationTimeoutException)
242273
{
243-
string context = Formatter.Format(interactions, FormattingOptions.MultipleLines);
244-
expectationBuilder.UpdateContexts(contexts => contexts.Add(
245-
new ResultContext.SyncCallback("Interactions", () => context)));
246-
_count = interactions.Length;
247-
return interactions.Length >= expected;
248-
})
249-
? Outcome.Success
250-
: Outcome.Failure;
251-
return this;
274+
string interactionsText = Formatter.Format(((IVerificationResult)actual).MockInteractions.Interactions,
275+
FormattingOptions.MultipleLines);
276+
expectationBuilder.UpdateContexts(contexts => contexts
277+
.Remove("Interactions")
278+
.Add(new ResultContext.SyncCallback("Interactions",
279+
() => interactionsText)));
280+
Outcome = Outcome.Failure;
281+
return this;
282+
}
252283
}
253284

254285
IVerificationResult result = actual;
@@ -257,8 +288,8 @@ public async Task<ConstraintResult> IsMetBy(VerificationResult<TVerify> actual,
257288
Outcome = result.Verify(interactions =>
258289
{
259290
string context = Formatter.Format(interactions, FormattingOptions.MultipleLines);
260-
expectationBuilder.UpdateContexts(contexts => contexts.Add(
261-
new ResultContext.SyncCallback("Interactions", () => context)));
291+
expectationBuilder.UpdateContexts(contexts => contexts
292+
.Add(new ResultContext.SyncCallback("Interactions", () => context)));
262293
_count = interactions.Length;
263294
return interactions.Length >= expected;
264295
})

Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastOnceTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,32 @@ async Task Act()
182182

183183
await That(Act).DoesNotThrow();
184184
}
185+
186+
[Fact]
187+
public async Task WhenNotInvoked_Within_ShouldFailWithDescriptiveMessage()
188+
{
189+
IMyService sut = IMyService.CreateMock();
190+
191+
sut.MyMethod(1, true);
192+
sut.MyMethod(2, true);
193+
194+
async Task Act()
195+
{
196+
await That(sut.Mock.Verify.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce().Within(50.Milliseconds());
197+
}
198+
199+
await That(Act).Throws<XunitException>()
200+
.WithMessage("""
201+
Expected that the aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService mock
202+
invoked method MyMethod(1, false) at least once,
203+
but never found it
204+
205+
Interactions:
206+
[
207+
[0] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, True),
208+
[1] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2, True)
209+
]
210+
""");
211+
}
185212
}
186213
}

Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,5 +208,34 @@ but never found it
208208
[]
209209
""");
210210
}
211+
212+
[Fact]
213+
public async Task WhenNotInvoked_Within_ShouldFailWithDescriptiveMessage()
214+
{
215+
IMyService sut = IMyService.CreateMock();
216+
217+
sut.MyMethod(1, true);
218+
sut.MyMethod(2, true);
219+
sut.MyMethod(3, true);
220+
221+
async Task Act()
222+
{
223+
await That(sut.Mock.Verify.MyMethod(It.IsAny<int>(), It.Is(true))).AtLeast(4).Within(50.Milliseconds());
224+
}
225+
226+
await That(Act).Throws<XunitException>()
227+
.WithMessage("""
228+
Expected that the aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService mock
229+
invoked method MyMethod(It.IsAny<int>(), true) at least 4 times,
230+
but found it only 3 times
231+
232+
Interactions:
233+
[
234+
[0] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, True),
235+
[1] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2, True),
236+
[2] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3, True)
237+
]
238+
""");
239+
}
211240
}
212241
}

Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTwiceTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,34 @@ async Task Act()
192192

193193
await That(Act).DoesNotThrow();
194194
}
195+
196+
[Fact]
197+
public async Task WhenNotInvoked_Within_ShouldFailWithDescriptiveMessage()
198+
{
199+
IMyService sut = IMyService.CreateMock();
200+
201+
sut.MyMethod(1, true);
202+
sut.MyMethod(2, true);
203+
sut.MyMethod(3, true);
204+
205+
async Task Act()
206+
{
207+
await That(sut.Mock.Verify.MyMethod(It.Is(2), It.Is(true))).AtLeastTwice().Within(50.Milliseconds());
208+
}
209+
210+
await That(Act).Throws<XunitException>()
211+
.WithMessage("""
212+
Expected that the aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService mock
213+
invoked method MyMethod(2, true) at least twice,
214+
but found it only once
215+
216+
Interactions:
217+
[
218+
[0] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, True),
219+
[1] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2, True),
220+
[2] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3, True)
221+
]
222+
""");
223+
}
195224
}
196225
}

Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.BetweenTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Threading;
22
using aweXpect.Chronology;
3+
using aweXpect.Core;
34
using Mockolate;
45
using Xunit.Sdk;
56

@@ -226,5 +227,34 @@ but found it {invocationTimes} times
226227
]
227228
""").AsWildcard();
228229
}
230+
231+
[Fact]
232+
public async Task WhenNotInvoked_Within_ShouldFailWithDescriptiveMessage()
233+
{
234+
IMyService sut = IMyService.CreateMock();
235+
236+
sut.MyMethod(1, true);
237+
sut.MyMethod(2, true);
238+
sut.MyMethod(3, true);
239+
240+
async Task Act()
241+
{
242+
await That(sut.Mock.Verify.MyMethod(It.IsAny<int>(), It.Is(true))).Between(4).And(6.Times()).Within(50.Milliseconds());
243+
}
244+
245+
await That(Act).Throws<XunitException>()
246+
.WithMessage("""
247+
Expected that the aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService mock
248+
invoked method MyMethod(It.IsAny<int>(), true) between 4 and 6 times,
249+
but found it only 3 times
250+
251+
Interactions:
252+
[
253+
[0] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, True),
254+
[1] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2, True),
255+
[2] invoke method global::aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3, True)
256+
]
257+
""");
258+
}
229259
}
230260
}

0 commit comments

Comments
 (0)