Skip to content

Commit dbac11e

Browse files
committed
Include the differences when using WithBodyEquivalentTo
1 parent 72f21fd commit dbac11e

File tree

6 files changed

+58
-35
lines changed

6 files changed

+58
-35
lines changed

Mockly.ApiVerificationTests/ApprovedApi/net472.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Mockly
1515
public string Query { get; }
1616
public System.Net.Http.HttpResponseMessage Response { get; }
1717
public string Scheme { get; }
18+
public int Sequence { get; set; }
1819
public System.DateTime Timestamp { get; }
1920
public System.Uri? Uri { get; }
2021
public System.Version Version { get; }

Mockly.ApiVerificationTests/ApprovedApi/net8.0.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace Mockly
1616
public string Query { get; }
1717
public System.Net.Http.HttpResponseMessage Response { get; }
1818
public string Scheme { get; }
19+
public int Sequence { get; set; }
1920
public System.DateTime Timestamp { get; }
2021
public System.Uri? Uri { get; }
2122
public System.Version Version { get; }

Mockly.Specs/AssertionSpecs.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,12 @@ public async Task Fails_when_body_is_not_equivalent_to_anonymous_object()
481481
.WithBodyEquivalentTo(expected);
482482

483483
// Assert
484-
act.Should().Throw<XunitException>();
484+
act.Should().Throw<XunitException>().WithMessage(
485+
"""
486+
Expected request #1 (POST https://localhost/api/test) to have a body equivalent to the expectation, but it did not:
487+
- Expected property actual.id to be 2, but found 1.
488+
- Expected property actual.name to be "y", but "x" differs near "x" (index 0).*
489+
""");
485490
}
486491
}
487492

@@ -500,17 +505,19 @@ public async Task Matches_body_having_properties_of_dictionary()
500505
await client.PostAsync("https://localhost/api/test", new StringContent("{ \"id\":\"1\", \"name\":\"x\" }"));
501506

502507
// Assert
503-
mock.Requests.Should().ContainRequest().WithBodyHavingPropertiesOf(new Dictionary<string, string>(StringComparer.Ordinal)
504-
{
505-
["id"] = "1",
506-
["name"] = "x"
507-
});
508+
mock.Requests.Should().ContainRequest().WithBodyHavingPropertiesOf(
509+
new Dictionary<string, string>(StringComparer.Ordinal)
510+
{
511+
["id"] = "1",
512+
["name"] = "x"
513+
});
508514

509-
mock.Requests.Should().ContainRequest().WithBodyHavingPropertiesOf(new Dictionary<string, string>(StringComparer.Ordinal)
510-
{
511-
["id"] = "2",
512-
["name"] = "y"
513-
});
515+
mock.Requests.Should().ContainRequest().WithBodyHavingPropertiesOf(
516+
new Dictionary<string, string>(StringComparer.Ordinal)
517+
{
518+
["id"] = "2",
519+
["name"] = "y"
520+
});
514521
}
515522

516523
[Fact]

Mockly/CapturedRequest.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,14 @@ public Version Version
107107
get => request.Version;
108108
}
109109

110+
/// <summary>
111+
/// The sequence number of the captured request, starting at 1.
112+
/// </summary>
113+
public int Sequence { get; set; }
114+
110115
public override string ToString()
111116
{
112-
string route = $"{Method} {Scheme}://{Host}{Path}{Query}";
113-
114-
return route;
117+
return $"{Method} {Scheme}://{Host}{Path}{Query}";
115118
}
116119

117120
}

Mockly/RequestCollection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class RequestCollection : IEnumerable<CapturedRequest>
1818
internal void Add(CapturedRequest request)
1919
{
2020
requests.Add(request);
21+
request.Sequence = requests.Count;
2122
}
2223

2324
/// <summary>

Shared/HttpMockAssertionExtensions.cs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,8 @@ public AndWhichConstraint<ContainedRequestAssertions, CapturedRequest> WithBodyE
492492
string because = "",
493493
params object[] becauseArgs)
494494
{
495-
string[] failures = [];
495+
string[] leastFailures = [];
496+
CapturedRequest? bestMatch = null;
496497
foreach (CapturedRequest request in requests)
497498
{
498499
T? actual = request.Body is null ? default : JsonSerializer.Deserialize<T>(request.Body!);
@@ -501,36 +502,42 @@ public AndWhichConstraint<ContainedRequestAssertions, CapturedRequest> WithBodyE
501502
using var scope = new AssertionScope();
502503
actual.Should().BeEquivalentTo(expected, because, becauseArgs);
503504

504-
failures = scope.Discard();
505+
var failures = scope.Discard();
505506
if (failures.Length == 0)
506507
{
507508
return new AndWhichConstraint<ContainedRequestAssertions, CapturedRequest>(this, request);
508509
}
510+
511+
if (leastFailures.Length == 0 || failures.Length < leastFailures.Length)
512+
{
513+
leastFailures = failures;
514+
bestMatch = request;
515+
}
509516
}
510517
}
511518

519+
string message;
512520
if (requests.Length == 1)
513521
{
514-
#if FA8
515-
AssertionChain.GetOrCreate()
516-
#else
517-
Execute.Assertion
518-
#endif
519-
.BecauseOf(because, becauseArgs)
520-
.FailWith(
521-
"Expected the request body to be equivalent to the expectation{because}, but it failed with: {0}",
522-
string.Join(Environment.NewLine, failures));
522+
message = "Expected request #{0} ({1}) to have a body equivalent to the expectation{because}, but it did not:";
523523
}
524524
else
525525
{
526+
message =
527+
"Expected the closest matching request #{0} ({1}) at have a body equivalent to the expectation{because}, but it did not:";
528+
}
529+
526530
#if FA8
527-
AssertionChain.GetOrCreate()
531+
AssertionChain.GetOrCreate()
528532
#else
529-
Execute.Assertion
533+
Execute.Assertion
530534
#endif
531-
.BecauseOf(because, becauseArgs)
532-
.FailWith("Expected at least one request body to be equivalent to the expected object{because}, but none were");
533-
}
535+
.BecauseOf(because, becauseArgs)
536+
.FailWith(message +
537+
Environment.NewLine +
538+
string.Join(Environment.NewLine, leastFailures.Select(failure => $"- {failure}")) +
539+
Environment.NewLine,
540+
bestMatch!.Sequence, bestMatch);
534541

535542
return new AndWhichConstraint<ContainedRequestAssertions, CapturedRequest>(this, []);
536543
}
@@ -582,15 +589,16 @@ public AndWhichConstraint<ContainedRequestAssertions, CapturedRequest> WithBodyH
582589
Execute.Assertion
583590
#endif
584591
.BecauseOf(because, becauseArgs)
585-
.FailWith("Expected the top-level properties of the request body to be equivalent to the provided dictionary{because}, but it failed with: ",
592+
.FailWith(
593+
"Expected the top-level properties of the request body to be equivalent to the provided dictionary{because}, but it failed with: ",
586594
string.Join(Environment.NewLine, failures));
587595
}
588596
else
589597
{
590598
#if FA8
591599
AssertionChain.GetOrCreate()
592600
#else
593-
Execute.Assertion
601+
Execute.Assertion
594602
#endif
595603
.BecauseOf(because, becauseArgs)
596604
.FailWith("Expected at least one request body to have the expected properties{because}, but none did");
@@ -645,18 +653,20 @@ public AndWhichConstraint<ContainedRequestAssertions, CapturedRequest> WithBodyH
645653
Execute.Assertion
646654
#endif
647655
.BecauseOf(because, becauseArgs)
648-
.FailWith("Expected the request body to contain property {0} with value {1}{because}, but it did not: {2}", key, value,
656+
.FailWith("Expected the request body to contain property {0} with value {1}{because}, but it did not: {2}", key,
657+
value,
649658
string.Join(Environment.NewLine, failures));
650659
}
651660
else
652661
{
653662
#if FA8
654663
AssertionChain.GetOrCreate()
655664
#else
656-
Execute.Assertion
665+
Execute.Assertion
657666
#endif
658667
.BecauseOf(because, becauseArgs)
659-
.FailWith("Expected at least one request body to contain property {0} with value {1}{because}, but none did", key, value);
668+
.FailWith("Expected at least one request body to contain property {0} with value {1}{because}, but none did", key,
669+
value);
660670
}
661671

662672
return new AndWhichConstraint<ContainedRequestAssertions, CapturedRequest>(this, []);

0 commit comments

Comments
 (0)