From be3a5cd03b443204d765ebe02099ab9d8d223c74 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Fri, 14 Nov 2025 22:56:28 +0100 Subject: [PATCH 01/12] Refactor tests to test methods based on the HttpRequestMessageAsserter, so that will be easier to refactor. --- .../Constructor.cs | 25 ++++++ .../WithFilter.cs} | 25 +----- .../WithRequestUri.cs | 78 +++++++++++++++++++ .../WithRequestUri.cs | 58 -------------- 4 files changed, 105 insertions(+), 81 deletions(-) create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/Constructor.cs rename test/TestableHttpClient.Tests/{HttpRequestMessageAsserterTests.cs => HttpRequestMessageAsserterTests/WithFilter.cs} (84%) create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestUri.cs diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/Constructor.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/Constructor.cs new file mode 100644 index 0000000..2cf4d0d --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/Constructor.cs @@ -0,0 +1,25 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class Constructor +{ + [Fact] + public void Constructor_NullRequestList_ThrowsArgumentNullException() + { + Assert.Throws(() => new HttpRequestMessageAsserter(null!)); + } + + [Fact] + public void Constructor_NullOptions_SetsDefault() + { + HttpRequestMessageAsserter sut = new([], null); + Assert.NotNull(sut.Options); + } + + [Fact] + public void Constructor_NotNullOptions_SetsOptions() + { + TestableHttpMessageHandlerOptions options = new(); + HttpRequestMessageAsserter sut = new([], options); + Assert.Same(options, sut.Options); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithFilter.cs similarity index 84% rename from test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests.cs rename to test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithFilter.cs index 4a4d31a..a8c5a66 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithFilter.cs @@ -1,28 +1,7 @@ -namespace TestableHttpClient.Tests; +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; -public class HttpRequestMessageAsserterTests +public sealed class WithFilter { - [Fact] - public void Constructor_NullRequestList_ThrowsArgumentNullException() - { - Assert.Throws(() => new HttpRequestMessageAsserter(null!)); - } - - [Fact] - public void Constructor_NullOptions_SetsDefault() - { - HttpRequestMessageAsserter sut = new([], null); - Assert.NotNull(sut.Options); - } - - [Fact] - public void Constructor_NotNullOptions_SetsOptions() - { - TestableHttpMessageHandlerOptions options = new(); - HttpRequestMessageAsserter sut = new([], options); - Assert.Same(options, sut.Options); - } - [Fact] public void WithFilter_NullPredicate_ThrowsArgumentNullException() { diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs new file mode 100644 index 0000000..12933fb --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs @@ -0,0 +1,78 @@ +using NSubstitute; + +using TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; + +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithRequestUri +{ + [Fact] + public void NullPattern_ThrowsArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithRequestUri(null!)); + + Assert.Equal("pattern", exception.ParamName); + } + + [Fact] + public void EmptyPattern_ThrowsArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithRequestUri(string.Empty)); + + Assert.Equal("pattern", exception.ParamName); + } + + [Fact] + public void WithPatternMatchingARequest_DoesNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request]); + + sut.WithRequestUri("https://example.com/"); + } + + [Fact] + public void WithPatternNotMatchingAnyRequest_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithRequestUri("https://no-op.com/")); + } + + + [Fact] + public void WithPatternNotMatchingAnyRequestWithCustomOptions_ThrowsHttpRequestMessageAssertionException() + { + TestableHttpMessageHandlerOptions options = new(); + options.UriPatternMatchingOptions.HostCaseInsensitive = false; + + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + + HttpRequestMessageAsserter sut = new([request], options); + + Assert.Throws(() => sut.WithRequestUri("https://EXAMPLE.com/")); + } + + [Fact] + public void WithPatternMatchingASingleRequest_DoesNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request]); + + sut.WithRequestUri("https://example.com/", 1); + } + + [Fact] + public void WithPatternMatchingMultipleRequestsButOnlyOneReceived_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithRequestUri("https://no-op.com/", 2)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestUri.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestUri.cs deleted file mode 100644 index cb88841..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestUri.cs +++ /dev/null @@ -1,58 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithRequestUri -{ - [Fact] - public void WithRequestUri_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithRequestUri("*")); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithRequestUri_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithRequestUri("*", 2)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithRequestUri_NullPattern_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithRequestUri(null!)); - - Assert.Equal("pattern", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithRequestUri_EmptyPattern_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithRequestUri(string.Empty)); - - Assert.Equal("pattern", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithRequestUri_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithRequestUri("https://example.com/"); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "uri pattern 'https://example.com/'"); - } -} From 7557d0c944b6aad60175f2b6e3378cd39e2c4f60 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sat, 15 Nov 2025 10:02:05 +0100 Subject: [PATCH 02/12] Move WithHttpMethod and WithHttpVersion tests --- .../HttpRequestMessageExtensions.cs | 28 ----- .../WithHttpMethod.cs | 74 +++++++++++++ .../WithHttpVersion.cs | 89 +++++++++++++++ .../WithRequestUri.cs | 31 +++--- .../HasHttpMethod.cs | 84 --------------- .../HasHttpVersion.cs | 102 ------------------ .../WithHttpMethod.cs | 68 ------------ .../WithHttpVersion.cs | 66 ------------ 8 files changed, 181 insertions(+), 361 deletions(-) create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpMethod.cs create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpVersion.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpMethod.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpVersion.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpMethod.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpVersion.cs diff --git a/src/TestableHttpClient/HttpRequestMessageExtensions.cs b/src/TestableHttpClient/HttpRequestMessageExtensions.cs index 691f07d..e5a7f3d 100644 --- a/src/TestableHttpClient/HttpRequestMessageExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessageExtensions.cs @@ -19,20 +19,6 @@ internal static bool HasHttpVersion(this HttpRequestMessage httpRequestMessage, return httpRequestMessage.Version == httpVersion; } - /// - /// Determines whether a specific HttpVersion is set on a request. - /// - /// A to check the correct version on. - /// The expected version. - /// true when the HttpVersion matches; otherwise, false. - internal static bool HasHttpVersion(this HttpRequestMessage httpRequestMessage, string httpVersion) - { - Guard.ThrowIfNull(httpRequestMessage); - Guard.ThrowIfNullOrEmpty(httpVersion); - - return httpRequestMessage.HasHttpVersion(new Version(httpVersion)); - } - /// /// Determines whether a specific HttpMethod is set on a request. /// @@ -47,20 +33,6 @@ internal static bool HasHttpMethod(this HttpRequestMessage httpRequestMessage, H return httpRequestMessage.Method == httpMethod; } - /// - /// Determines whether a specific HttpMethod is set on a request. - /// - /// A to check the correct method on. - /// The expected method. - /// true when the HttpMethod matches; otherwise, false. - internal static bool HasHttpMethod(this HttpRequestMessage httpRequestMessage, string httpMethod) - { - Guard.ThrowIfNull(httpRequestMessage); - Guard.ThrowIfNullOrEmpty(httpMethod); - - return httpRequestMessage.HasHttpMethod(new HttpMethod(httpMethod)); - } - /// /// Determines whether a specific header is set on a request. /// diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpMethod.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpMethod.cs new file mode 100644 index 0000000..31ff6fe --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpMethod.cs @@ -0,0 +1,74 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithHttpMethod +{ + [Fact] + public void WithNullHttpMethod_ThrowsArgumentNullException() + { + // Arrange + var sut = new HttpRequestMessageAsserter(Array.Empty()); + // Act + var exception = Assert.Throws(() => sut.WithHttpMethod(null!)); + // Assert + Assert.Equal("httpMethod", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_WithNullHttpMethod_ThrowsArgumentNullException() + { + // Arrange + var sut = new HttpRequestMessageAsserter(Array.Empty()); + // Act + var exception = Assert.Throws(() => sut.WithHttpMethod(null!, 1)); + // Assert + Assert.Equal("httpMethod", exception.ParamName); + } + + [Fact] + public void WithMatchingHttpMethod_ShouldNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithHttpMethod(HttpMethod.Get); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithMatchingHttpMethod_ShouldNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithHttpMethod(HttpMethod.Get, 2); + } + + [Fact] + public void WithNonMatchingNumberOfRequests_WithMatchingHttpMethod_ShouldNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHttpMethod(HttpMethod.Get, 1)); + } + + [Fact] + public void WithNonMatchingHttpMethod_ShouldThrowHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHttpMethod(HttpMethod.Post)); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithNonMatchingHttpMethod_ShouldThrowHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHttpMethod(HttpMethod.Post, 2)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpVersion.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpVersion.cs new file mode 100644 index 0000000..10afda4 --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHttpVersion.cs @@ -0,0 +1,89 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithHttpVersion +{ + [Fact] + public void NullHttpVersion_ThrowsArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHttpVersion(null!)); + + Assert.Equal("httpVersion", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_NullHttpVersion_ThrowsArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHttpVersion(null!, 1)); + + Assert.Equal("httpVersion", exception.ParamName); + } + + [Fact] + public void WithMatchingVersion_DoesNotThrow() + { + using HttpRequestMessage request = new() + { + Version = HttpVersion.Version11 + }; + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithHttpVersion(HttpVersion.Version11); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithMatchingVersions_DoesNotThrow() + { + using HttpRequestMessage request = new() + { + Version = HttpVersion.Version11 + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithHttpVersion(HttpVersion.Version11, 2); + } + + [Fact] + public void WithNonMatchingVersion_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new() + { + Version = HttpVersion.Version10 + }; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHttpVersion(HttpVersion.Version11)); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithNonMatchingVersion_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new() + { + Version = HttpVersion.Version10 + }; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHttpVersion(HttpVersion.Version11, 1)); + } + + [Fact] + public void WithNonMatchingNumberOfRequests_WithMatchingVersion_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new() + { + Version = HttpVersion.Version11 + }; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHttpVersion(HttpVersion.Version11, 2)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs index 12933fb..7301a77 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs @@ -1,13 +1,9 @@ -using NSubstitute; - -using TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; public sealed class WithRequestUri { [Fact] - public void NullPattern_ThrowsArgumentNullException() + public void NullRequestUri_ThrowsArgumentNullException() { HttpRequestMessageAsserter sut = new([]); @@ -17,7 +13,7 @@ public void NullPattern_ThrowsArgumentNullException() } [Fact] - public void EmptyPattern_ThrowsArgumentException() + public void EmptyRequestUri_ThrowsArgumentException() { HttpRequestMessageAsserter sut = new([]); @@ -27,7 +23,7 @@ public void EmptyPattern_ThrowsArgumentException() } [Fact] - public void WithPatternMatchingARequest_DoesNotThrow() + public void WithMatchingRequestUri_DoesNotThrow() { using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); HttpRequestMessageAsserter sut = new([request]); @@ -36,7 +32,7 @@ public void WithPatternMatchingARequest_DoesNotThrow() } [Fact] - public void WithPatternNotMatchingAnyRequest_ThrowsHttpRequestMessageAssertionException() + public void WithNonMatchingRequestUri_ThrowsHttpRequestMessageAssertionException() { using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); HttpRequestMessageAsserter sut = new([request]); @@ -46,7 +42,7 @@ public void WithPatternNotMatchingAnyRequest_ThrowsHttpRequestMessageAssertionEx [Fact] - public void WithPatternNotMatchingAnyRequestWithCustomOptions_ThrowsHttpRequestMessageAssertionException() + public void WithNonMatchinRequestUri_WithCustomOptions_ThrowsHttpRequestMessageAssertionException() { TestableHttpMessageHandlerOptions options = new(); options.UriPatternMatchingOptions.HostCaseInsensitive = false; @@ -59,16 +55,25 @@ public void WithPatternNotMatchingAnyRequestWithCustomOptions_ThrowsHttpRequestM } [Fact] - public void WithPatternMatchingASingleRequest_DoesNotThrow() + public void WithMatchingNumberOfRequests_WithMatchingRequestUri_DoesNotThrow() + { + using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithRequestUri("https://example.com/", 2); + } + + [Fact] + public void WithNonMatchingNumberOfRequest_WithMatchingRequestUris_ThrowsHttpRequestMessageAssertionException() { using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); HttpRequestMessageAsserter sut = new([request]); - sut.WithRequestUri("https://example.com/", 1); + Assert.Throws(() => sut.WithRequestUri("https://example.com/", 2)); } [Fact] - public void WithPatternMatchingMultipleRequestsButOnlyOneReceived_ThrowsHttpRequestMessageAssertionException() + public void WithNonMatchingNumberOfRequest_WithNonMatchingRequestUris_ThrowsHttpRequestMessageAssertionException() { using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); HttpRequestMessageAsserter sut = new([request]); diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpMethod.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpMethod.cs deleted file mode 100644 index 14a9a11..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpMethod.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - public void HasHttpMethod_WithHttpMethod_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasHttpMethod(HttpMethod.Get)); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasHttpMethod_WithString_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasHttpMethod("GET")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasHttpMethod_WithHttpMethod_NullHttpMethod_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - var exception = Assert.Throws(() => sut.HasHttpMethod((HttpMethod)null!)); - Assert.Equal("httpMethod", exception.ParamName); - } - - [Fact] - public void HasHttpMethod_WithString_NullHttpMethod_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - var exception = Assert.Throws(() => sut.HasHttpMethod((string)null!)); - Assert.Equal("httpMethod", exception.ParamName); - } - - [Fact] - public void HasHttpMethod_WithString_EmptyHttpMethod_ThrowsArgumentException() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - var exception = Assert.Throws(() => sut.HasHttpMethod(string.Empty)); - Assert.Equal("httpMethod", exception.ParamName); - } - - [Fact] - public void HasHttpMethod_WithHttpMethod_CorrectHttpMethod_ReturnsTrue() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - Assert.True(sut.HasHttpMethod(HttpMethod.Get)); - } - - [Theory] - [InlineData("GET")] - [InlineData("Get")] - [InlineData("get")] - public void HasHttpMethod_WithString_CorrectHttpMethod_ReturnsTrue(string httpMethod) - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - Assert.True(sut.HasHttpMethod(httpMethod)); - } - - [Fact] - public void HasHttpMethod_WithHttpMethod_IncorrectHttpMethod_ReturnsFalse() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - Assert.False(sut.HasHttpMethod(HttpMethod.Put)); - } - - [Fact] - public void HasHttpMethod_WithString_IncorrectHttpMethod_ReturnsFalse() - { - using HttpRequestMessage sut = new() { Method = HttpMethod.Get }; - - Assert.False(sut.HasHttpMethod("DELETE")); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpVersion.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpVersion.cs deleted file mode 100644 index afc7349..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasHttpVersion.cs +++ /dev/null @@ -1,102 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - public void HasHttpVersion_WithVersion_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasHttpVersion(HttpVersion.Version11)); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasHttpVersion_WithString_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasHttpVersion("1.1")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasHttpVersion_WithVersion_NullVersion_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new() - { -#if NETFRAMEWORK - Version = new Version(0, 0) -#else - Version = HttpVersion.Unknown -#endif - }; - - var exception = Assert.Throws(() => sut.HasHttpVersion((Version)null!)); - Assert.Equal("httpVersion", exception.ParamName); - } - - [Fact] - public void HasHttpVersion_WithString_NullVersion_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new() - { -#if NETFRAMEWORK - Version = new Version(0, 0) -#else - Version = HttpVersion.Unknown -#endif - }; - - var exception = Assert.Throws(() => sut.HasHttpVersion((string)null!)); - Assert.Equal("httpVersion", exception.ParamName); - } - - [Fact] - public void HasHttpVersion_WithString_EmptyVersion_ThrowsArgumentException() - { - using HttpRequestMessage sut = new() - { -#if NETFRAMEWORK - Version = new Version(0, 0) -#else - Version = HttpVersion.Unknown -#endif - }; - - var exception = Assert.Throws(() => sut.HasHttpVersion(string.Empty)); - Assert.Equal("httpVersion", exception.ParamName); - } - - [Fact] - public void HasHttpVersion_WithVersion_CorrectVersion_ReturnsTrue() - { - using var sut = new HttpRequestMessage { Version = HttpVersion.Version11 }; - - Assert.True(sut.HasHttpVersion(HttpVersion.Version11)); - } - - [Fact] - public void HasHttpVersion_WithString_CorrectVersion_ReturnsTrue() - { - using HttpRequestMessage sut = new() { Version = HttpVersion.Version11 }; - - Assert.True(sut.HasHttpVersion("1.1")); - } - - [Fact] - public void HasHttpVersion_WithVersion_IncorrectVersion_ReturnsFalse() - { - using HttpRequestMessage sut = new() { Version = HttpVersion.Version11 }; - - Assert.False(sut.HasHttpVersion(HttpVersion.Version10)); - } - - [Fact] - public void HasHttpVersion_WithString_IncorrectVersion_ReturnsFalse() - { - using HttpRequestMessage sut = new() { Version = HttpVersion.Version11 }; - - Assert.False(sut.HasHttpVersion("1.0")); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpMethod.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpMethod.cs deleted file mode 100644 index 0e93001..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpMethod.cs +++ /dev/null @@ -1,68 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithHttpMethod -{ - [Fact] - public void WithHttpMethod_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHttpMethod(HttpMethod.Get)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHttpMethod_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHttpMethod(HttpMethod.Get, 1)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHttpMethod_WithoutNumberOfRequests_NullHttpMethod_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHttpMethod(null!)); - - Assert.Equal("httpMethod", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHttpMethod_WithNumberOfRequests_NullHttpMethod_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHttpMethod(null!, 1)); - - Assert.Equal("httpMethod", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHttpMethod_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHttpMethod(HttpMethod.Get); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "HTTP Method 'GET'"); - } - - [Fact] - public void WithHttpMethod_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHttpMethod(HttpMethod.Get, 1); - - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "HTTP Method 'GET'"); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpVersion.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpVersion.cs deleted file mode 100644 index 331be92..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHttpVersion.cs +++ /dev/null @@ -1,66 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithHttpVersion -{ - [Fact] - public void WithHttpVersion_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNulLException() - { - IHttpRequestMessagesCheck sut = null!; - var exception = Assert.Throws(() => sut.WithHttpVersion(HttpVersion.Version11)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHttpVersion_WithNumberOfRequests_NullCheck_ThrowsArgumentNulLException() - { - IHttpRequestMessagesCheck sut = null!; - var exception = Assert.Throws(() => sut.WithHttpVersion(HttpVersion.Version11, 1)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHttpVersion_WithoutNumberOfRequests_NullHttpVersion_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHttpVersion(null!)); - - Assert.Equal("httpVersion", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHttpVersion_WithNumberOfRequests_NullHttpVersion_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHttpVersion(null!, 1)); - - Assert.Equal("httpVersion", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHttpVersion_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHttpVersion(HttpVersion.Version11); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "HTTP Version '1.1'"); - } - - [Fact] - public void WithHttpVersion_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHttpVersion(HttpVersion.Version11, 1); - - sut.Received().WithFilter(Args.AnyPredicate(), (int?)1, "HTTP Version '1.1'"); - } -} From c86aa34990aec858b27d2abda04b70df6e4af89f Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sat, 15 Nov 2025 16:28:05 +0100 Subject: [PATCH 03/12] Deprecate HasRequestHeader and HasResponseHeader and move tests for WithHeader to HttpRequestMessageAsserter; --- .../HttpRequestMessageExtensions.cs | 51 ++--- .../HttpRequestMessagesCheckExtensions.cs | 20 +- .../AssertingRequests.cs | 30 +-- .../CreatingClients.cs | 2 +- .../WithHeaderName.cs | 117 ++++++++++++ .../WithHeaderNameAndValue.cs | 179 ++++++++++++++++++ .../HasContentHeader.cs | 15 ++ .../HasRequestHeader.cs | 13 ++ .../WithContentHeaderName.cs | 1 + .../WithContentHeaderNameAndValue.cs | 1 + .../WithHeaderName.cs | 90 --------- .../WithHeaderNameAndValue.cs | 134 ------------- .../WithRequestHeaderName.cs | 1 + .../WithRequestHeaderNameAndValue.cs | 1 + 14 files changed, 381 insertions(+), 274 deletions(-) create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderName.cs create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderNameAndValue.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderName.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderNameAndValue.cs diff --git a/src/TestableHttpClient/HttpRequestMessageExtensions.cs b/src/TestableHttpClient/HttpRequestMessageExtensions.cs index e5a7f3d..29bab72 100644 --- a/src/TestableHttpClient/HttpRequestMessageExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessageExtensions.cs @@ -33,13 +33,24 @@ internal static bool HasHttpMethod(this HttpRequestMessage httpRequestMessage, H return httpRequestMessage.Method == httpMethod; } - /// - /// Determines whether a specific header is set on a request. - /// - /// This method only checks headers in - /// A to check the request header on. - /// The name of the header to locate on the request. - /// true when the request contains a header with the specified name; otherwise, false. + internal static bool HasHeader(this HttpRequestMessage httpRequestMessage, string headerName) + { + Guard.ThrowIfNull(httpRequestMessage); + Guard.ThrowIfNullOrEmpty(headerName); + + return httpRequestMessage.Headers.HasHeader(headerName) || (httpRequestMessage.Content is not null && httpRequestMessage.Content.Headers.HasHeader(headerName)); + } + + internal static bool HasHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue) + { + Guard.ThrowIfNull(httpRequestMessage); + Guard.ThrowIfNullOrEmpty(headerName); + Guard.ThrowIfNullOrEmpty(headerValue); + + return httpRequestMessage.Headers.HasHeader(headerName, headerValue) || (httpRequestMessage.Content is not null && httpRequestMessage.Content.Headers.HasHeader(headerName, headerValue)); + } + + [Obsolete("Use HasHeader instead.")] internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage, string headerName) { Guard.ThrowIfNull(httpRequestMessage); @@ -48,14 +59,7 @@ internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage return httpRequestMessage.Headers.HasHeader(headerName); } - /// - /// Determines whether a specific header with a specific value is set on a request. - /// - /// This method only checks headers in - /// A to check the request header on. - /// The name of the header to locate on the request. - /// The value the header should have. Wildcard is supported. - /// true when the request contains a header with the specified name and value; otherwise, false. + [Obsolete("Use HasHeader instead.")] internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue) { Guard.ThrowIfNull(httpRequestMessage); @@ -65,13 +69,7 @@ internal static bool HasRequestHeader(this HttpRequestMessage httpRequestMessage return httpRequestMessage.Headers.HasHeader(headerName, headerValue); } - /// - /// Determines whether a specific header is set on a request. - /// - /// This method only checks headers in - /// A to check the content header on. - /// The name of the header to locate on the request content. - /// true when the request contains a header with the specified name; otherwise, false. + [Obsolete("Use HasHeader instead.")] internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage, string headerName) { Guard.ThrowIfNull(httpRequestMessage); @@ -85,14 +83,7 @@ internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage return httpRequestMessage.Content.Headers.HasHeader(headerName); } - /// - /// Determines whether a specific header with a specific value is set on a request. - /// - /// This method only checks headers in - /// A to check the content header on. - /// The name of the header to locate on the request content. - /// The value the header should have. Wildcard is supported. - /// true when the request contains a header with the specified name and value; otherwise, false. + [Obsolete("Use HasHeader instead.")] internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage, string headerName, string headerValue) { Guard.ThrowIfNull(httpRequestMessage); diff --git a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs index fd55d7f..fff199c 100644 --- a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs @@ -92,6 +92,7 @@ private static IHttpRequestMessagesCheck WithHttpVersion(this IHttpRequestMessag /// The implementation that hold all the request messages. /// The name of the header that is expected. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName) => WithRequestHeader(check, headerName, (int?)null); /// @@ -102,8 +103,10 @@ private static IHttpRequestMessagesCheck WithHttpVersion(this IHttpRequestMessag /// The name of the header that is expected. /// The expected number of requests. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) => WithRequestHeader(check, headerName, (int?)expectedNumberOfRequests); + [Obsolete("Use WithHeader instead.")] private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, int? expectedNumberOfRequests) { Guard.ThrowIfNull(check); @@ -120,6 +123,7 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The name of the header that is expected. /// The value of the expected header, supports wildcards. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) => WithRequestHeader(check, headerName, headerValue, null); /// @@ -131,8 +135,10 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The value of the expected header, supports wildcards. /// The expected number of requests. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) => WithRequestHeader(check, headerName, headerValue, (int?)expectedNumberOfRequests); + [Obsolete("Use WithHeader instead.")] private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int? expectedNumberOfRequests) { Guard.ThrowIfNull(check); @@ -149,6 +155,7 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The implementation that hold all the request messages. /// The name of the header that is expected. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName) => WithContentHeader(check, headerName, (int?)null); /// @@ -159,8 +166,10 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The name of the header that is expected. /// The expected number of requests. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) => WithContentHeader(check, headerName, (int?)expectedNumberOfRequests); + [Obsolete("Use WithHeader instead.")] private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, int? expectedNumberOfRequests) { Guard.ThrowIfNull(check); @@ -177,6 +186,7 @@ private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMess /// The name of the header that is expected. /// The value of the expected header, supports wildcards. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) => WithContentHeader(check, headerName, headerValue, null); /// @@ -188,8 +198,10 @@ private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMess /// The value of the expected header, supports wildcards. /// The expected number of requests. /// The for further assertions. + [Obsolete("Use WithHeader instead.")] public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) => WithContentHeader(check, headerName, headerValue, (int?)expectedNumberOfRequests); + [Obsolete("Use WithHeader instead.")] private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int? expectedNumberOfRequests) { Guard.ThrowIfNull(check); @@ -221,7 +233,7 @@ private static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesChe Guard.ThrowIfNull(check); Guard.ThrowIfNullOrEmpty(headerName); - return check.WithFilter(x => x.HasRequestHeader(headerName) || x.HasContentHeader(headerName), expectedNumberOfRequests, $"header '{headerName}'"); + return check.WithFilter(x => x.HasHeader(headerName), expectedNumberOfRequests, $"header '{headerName}'"); } /// @@ -249,7 +261,7 @@ private static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesChe Guard.ThrowIfNullOrEmpty(headerName); Guard.ThrowIfNullOrEmpty(headerValue); - return check.WithFilter(x => x.HasRequestHeader(headerName, headerValue) || x.HasContentHeader(headerName, headerValue), expectedNumberOfRequests, $"header '{headerName}' and value '{headerValue}'"); + return check.WithFilter(x => x.HasHeader(headerName, headerValue), expectedNumberOfRequests, $"header '{headerName}' and value '{headerValue}'"); } /// @@ -319,7 +331,7 @@ private static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessag var jsonString = JsonSerializer.Serialize(jsonObject, jsonSerializerOptions ?? check.Options.JsonSerializerOptions); - return check.WithFilter(x => x.HasContent(jsonString) && x.HasContentHeader("Content-Type", "application/json*"), expectedNumberOfRequests, $"json content '{jsonString}'"); + return check.WithFilter(x => x.HasContent(jsonString) && x.HasHeader("Content-Type", "application/json*"), expectedNumberOfRequests, $"json content '{jsonString}'"); } /// @@ -347,6 +359,6 @@ private static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpReq using var content = new FormUrlEncodedContent(nameValueCollection); var contentString = content.ReadAsStringAsync().Result; - return check.WithFilter(x => x.HasContent(contentString) && x.HasContentHeader("Content-Type", "application/x-www-form-urlencoded*"), expectedNumberOfRequests, $"form url encoded content '{contentString}'"); + return check.WithFilter(x => x.HasContent(contentString) && x.HasHeader("Content-Type", "application/x-www-form-urlencoded*"), expectedNumberOfRequests, $"form url encoded content '{contentString}'"); } } diff --git a/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs b/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs index 5f1f317..4e539a4 100644 --- a/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs +++ b/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs @@ -117,20 +117,20 @@ public async Task AssertingHttpMethods() } [Fact] - public async Task AssertingRequestHeaders() + public async Task AssertingHeaders() { using TestableHttpMessageHandler testHandler = new(); using HttpClient client = new(testHandler); client.DefaultRequestHeaders.Add("api-version", "1.0"); _ = await client.GetAsync("https://httpbin.org/get", TestContext.Current.CancellationToken); - testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version"); - testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1.0"); - testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1*"); + testHandler.ShouldHaveMadeRequests().WithHeader("api-version"); + testHandler.ShouldHaveMadeRequests().WithHeader("api-version", "1.0"); + testHandler.ShouldHaveMadeRequests().WithHeader("api-version", "1*"); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("my-version")); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "1")); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithRequestHeader("api-version", "2*")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("my-version")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("api-version", "1")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("api-version", "2*")); } [Fact] @@ -142,15 +142,15 @@ public async Task AssertingContentHeaders() using StringContent content = new("", Encoding.UTF8, "application/json"); _ = await client.PostAsync("https://httpbin.org/post", content, TestContext.Current.CancellationToken); - testHandler.ShouldHaveMadeRequests().WithContentHeader("content-type"); - testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type"); - testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json; charset=utf-8"); - testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json*"); - testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "*charset=utf-8"); + testHandler.ShouldHaveMadeRequests().WithHeader("content-type"); + testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type"); + testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type", "application/json; charset=utf-8"); + testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type", "application/json*"); + testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type", "*charset=utf-8"); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Disposition")); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "application/json")); - Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithContentHeader("Content-Type", "*=utf-16")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("Content-Disposition")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type", "application/json")); + Assert.Throws(() => testHandler.ShouldHaveMadeRequests().WithHeader("Content-Type", "*=utf-16")); } [Fact] diff --git a/test/TestableHttpClient.IntegrationTests/CreatingClients.cs b/test/TestableHttpClient.IntegrationTests/CreatingClients.cs index a22aab6..41afcf4 100644 --- a/test/TestableHttpClient.IntegrationTests/CreatingClients.cs +++ b/test/TestableHttpClient.IntegrationTests/CreatingClients.cs @@ -23,7 +23,7 @@ public async Task CreateClientWithConfiguration() await client.GetAsync("https://httpbin.org/get", TestContext.Current.CancellationToken); - testableHttpMessageHandler.ShouldHaveMadeRequests().WithRequestHeader("test", "test"); + testableHttpMessageHandler.ShouldHaveMadeRequests().WithHeader("test", "test"); } [Fact] diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderName.cs new file mode 100644 index 0000000..e9ea7fb --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderName.cs @@ -0,0 +1,117 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithHeaderName +{ + [Fact] + public void NullHeaderName_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(null!)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void EmptyHeaderName_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(string.Empty)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_NullHeaderName_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(null!, 1)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_EmptyHeaderName_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(string.Empty, 1)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithMatchingHeaderName_DoesNotThrow(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithHeader(headerName); + } + + [Fact] + public void WithNotMatchingHeaderName_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHeader("Content-Type")); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithMatchingNumberOfRequests_WithMatchingHeaderName_DoesNotThrow(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithHeader(headerName, 2); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithNotMatchingHeaderName_DoesNotThrow() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHeader("Content-Type", 2)); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithNotMatchingNumberOfRequests_WithMatchingHeaderName_ThrowsHttpRequestMessageAssertionException(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHeader(headerName, 1)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderNameAndValue.cs new file mode 100644 index 0000000..252e7c6 --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithHeaderNameAndValue.cs @@ -0,0 +1,179 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithHeaderNameAndValue +{ + [Fact] + public void NullHeaderNameAndFilledValue_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(null!, "value")); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void EmptyHeaderNameAndFilledValue_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(string.Empty, "value")); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void FilledHeaderNameAndNullValue_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader("header", null!)); + + Assert.Equal("headerValue", exception.ParamName); + } + + [Fact] + public void FilledHeaderNameAndEmptyValue_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader("header", string.Empty)); + + Assert.Equal("headerValue", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_NullHeaderNameAndFilledValue_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(null!, "value", 1)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_EmptyHeaderNameAndFilledValue_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader(string.Empty, "value", 1)); + + Assert.Equal("headerName", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_FilledHeaderNameAndNullValue_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader("header", null!, 1)); + + Assert.Equal("headerValue", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_FilledHeaderNameAndEmptyValue_ShouldThrowArgumentException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithHeader("header", string.Empty, 1)); + + Assert.Equal("headerValue", exception.ParamName); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithMatchingHeaderNameAndMatchingValue_DoesNotThrow(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithHeader(headerName, "application/json"); + } + + [Fact] + public void WithNotMatchingHeaderNameAndMatchingValue_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHeader("Content-Type", "localhost")); + } + + [Fact] + public void WithMatchingHeaderNameAnNotdMatchingValue_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithHeader("host", "application/json")); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithMatchingNumberOfRequests_WithMatchingHeaderNameAndMatchingValue_DoesNotThrow(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithHeader(headerName, "application/json", 2); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithNotMatchingHeaderNameAndMatchingValue_DoesNotThrow() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHeader("Content-Type", "localhost", 2)); + } + + [Fact] + public void WithMatchingNumberOfRequests_WithMatchingHeaderNameAndNotMatchingValue_DoesNotThrow() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "localhost"; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHeader("host", "application/json", 2)); + } + + [Theory] + [InlineData("Content-Type")] + [InlineData("content-type")] + [InlineData("CONTENT-type")] + public void WithNotMatchingNumberOfRequests_WithMatchingHeaderNameAndMatchingValue_ThrowsHttpRequestMessageAssertionException(string headerName) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithHeader(headerName, "application/json", 1)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs index 50db146..7ab243d 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs @@ -3,6 +3,7 @@ namespace TestableHttpClient.Tests; public partial class HttpRequestMessageExtensionsTests { [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NullRequest_ThrowsArgumentNullException() { HttpRequestMessage sut = null!; @@ -12,6 +13,7 @@ public void HasContentHeader_NullRequest_ThrowsArgumentNullException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NullHeaderName_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -21,6 +23,7 @@ public void HasContentHeader_NullHeaderName_ThrowsArgumentNullException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_EmptyHeaderName_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -30,6 +33,7 @@ public void HasContentHeader_EmptyHeaderName_ThrowsArgumentException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() { HttpRequestMessage sut = null!; @@ -39,6 +43,7 @@ public void HasContentHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_T } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -47,6 +52,7 @@ public void HasContentHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentN } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -55,6 +61,7 @@ public void HasContentHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgument } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -63,6 +70,7 @@ public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentN } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -71,6 +79,7 @@ public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentE } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_ExistingHeaderName_ReturnsTrue() { using HttpRequestMessage sut = new() @@ -85,6 +94,7 @@ public void HasContentHeader_ExistingHeaderName_ReturnsTrue() [Theory] [InlineData("Host")] [InlineData("Content-Disposition")] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NotExistingHeaderName_ReturnsFalse(string headerName) { using HttpRequestMessage sut = new() @@ -96,6 +106,7 @@ public void HasContentHeader_NotExistingHeaderName_ReturnsFalse(string headerNam } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NoContent_ReturnsFalse() { using HttpRequestMessage sut = new() @@ -111,6 +122,7 @@ public void HasContentHeader_NoContent_ReturnsFalse() [InlineData("inline; *")] [InlineData("*; filename=empty.file")] [InlineData("*")] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string value) { using HttpRequestMessage sut = new() @@ -126,6 +138,7 @@ public void HasContentHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NotExitingHeaderNameAndValue_ReturnsFalse() { using HttpRequestMessage sut = new() @@ -137,6 +150,7 @@ public void HasContentHeader_NotExitingHeaderNameAndValue_ReturnsFalse() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_NullContentNotExitingHeaderNameAndValue_ReturnsFalse() { using HttpRequestMessage sut = new() @@ -151,6 +165,7 @@ public void HasContentHeader_NullContentNotExitingHeaderNameAndValue_ReturnsFals [InlineData("inline; filename=emtpy.file")] [InlineData("inline; *")] [InlineData("*; filename=empty.file")] + [Obsolete("Use HasHeader instead.")] public void HasContentHeader_ExistingHeaderNameNotMatchingValue_ReturnsFalse(string value) { using HttpRequestMessage sut = new() diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs index 405b314..b567504 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs @@ -3,6 +3,7 @@ namespace TestableHttpClient.Tests; public partial class HttpRequestMessageExtensionsTests { [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NullRequest_ThrowsArgumentNullException() { HttpRequestMessage sut = null!; @@ -12,6 +13,7 @@ public void HasRequestHeader_NullRequest_ThrowsArgumentNullException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NullHeaderName_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -21,6 +23,7 @@ public void HasRequestHeader_NullHeaderName_ThrowsArgumentNullException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_EmptyHeaderName_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -30,6 +33,7 @@ public void HasRequestHeader_EmptyHeaderName_ThrowsArgumentException() } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() { HttpRequestMessage sut = null!; @@ -39,6 +43,7 @@ public void HasRequestHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_T } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -47,6 +52,7 @@ public void HasRequestHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentN } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -55,6 +61,7 @@ public void HasRequestHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgument } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentNullException() { using HttpRequestMessage sut = new(); @@ -63,6 +70,7 @@ public void HasRequestHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentN } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NonNullHeaderNameAndEmptyHeaderValue_ThrowsArgumentException() { using HttpRequestMessage sut = new(); @@ -71,6 +79,7 @@ public void HasRequestHeader_NonNullHeaderNameAndEmptyHeaderValue_ThrowsArgument } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_ExistingHeaderName_ReturnsTrue() { using HttpRequestMessage sut = new(); @@ -82,6 +91,7 @@ public void HasRequestHeader_ExistingHeaderName_ReturnsTrue() [Theory] [InlineData("Host")] [InlineData("Content-Type")] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NotExistingHeaderName_ReturnsFalse(string headerName) { using HttpRequestMessage sut = new(); @@ -94,6 +104,7 @@ public void HasRequestHeader_NotExistingHeaderName_ReturnsFalse(string headerNam [InlineData("example*")] [InlineData("*.com")] [InlineData("*")] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string value) { using HttpRequestMessage sut = new(); @@ -103,6 +114,7 @@ public void HasRequestHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string } [Fact] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_NotExitingHeaderNameAndValue_ReturnsFalse() { using HttpRequestMessage sut = new(); @@ -114,6 +126,7 @@ public void HasRequestHeader_NotExitingHeaderNameAndValue_ReturnsFalse() [InlineData("example.com")] [InlineData("example*")] [InlineData("*.com")] + [Obsolete("Use HasHeader instead.")] public void HasRequestHeader_ExistingHeaderNameNotMatchingValue_ReturnsFalse(string value) { using HttpRequestMessage sut = new(); diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs index 6ca3401..4bf4191 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs @@ -2,6 +2,7 @@ namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +[Obsolete("Use WithHeader")] public class WithContentHeaderName { [Fact] diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs index 0748b99..bfb8b02 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs @@ -2,6 +2,7 @@ namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +[Obsolete("Use WithHeader")] public class WithContentHeaderNameAndValue { [Fact] diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderName.cs deleted file mode 100644 index ea2d2b0..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderName.cs +++ /dev/null @@ -1,90 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithHeaderName -{ - [Fact] - public void WithHeader_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHeader("someHeader")); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHeader_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", 1)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHeader_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(null!)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeader_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(string.Empty)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeader_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(null!, 1)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeader_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(string.Empty, 1)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeader_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHeader("Content-Type"); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "header 'Content-Type'"); - } - - [Fact] - public void WithHeader_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHeader("Content-Type", 1); - - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "header 'Content-Type'"); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderNameAndValue.cs deleted file mode 100644 index 91fe469..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithHeaderNameAndValue.cs +++ /dev/null @@ -1,134 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithHeaderNameAndValue -{ - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", "someValue")); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", "someValue", 1)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(null!, "someValue")); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(string.Empty, "someValue")); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(null!, "someValue", 1)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader(string.Empty, "someValue", 1)); - - Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_NullValue_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", null!)); - - Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_EmptyValue_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", string.Empty)); - - Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_NullValue_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", null!, 1)); - - Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_EmptyValue_ThrowsArgumentException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithHeader("someHeader", string.Empty, 1)); - - Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithHeaderNameAndValue_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHeader("someHeader", "someValue"); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "header 'someHeader' and value 'someValue'"); - } - - [Fact] - public void WithHeaderNameAndValue_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithHeader("someHeader", "someValue", 1); - - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "header 'someHeader' and value 'someValue'"); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs index 5b60412..649b7c1 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs @@ -2,6 +2,7 @@ namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +[Obsolete("Use WithHeader")] public class WithRequestHeaderName { [Fact] diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs index 090c9f6..e76511a 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs @@ -2,6 +2,7 @@ namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +[Obsolete("Use WithHeader")] public class WithRequestHeaderNameAndValue { [Fact] From 707bef89400163151c21eebd4715f5422136a5fb Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sat, 15 Nov 2025 16:30:11 +0100 Subject: [PATCH 04/12] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a51fa2b..8a584a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.12] - unplanned +### Deprecated +- The methods `WithRequestHeader` and `WithContentHeader` are deprecated, please use `WithHeader` instead. + ### Removed - .NET 6.0 target, since it is no longer supported - .NET Framework 4.6.2, 4.7.0 and 4.7.2, since these can't be tested using xUnit v3 - automatic nuget updates by dependabot, since we want to test against the lowest supported nuget version and most of the time dependabot does not choose the right package. + ### Added - Support for .NET 9.0 - Support for .NET 10.0 + ### Changed - The TestableHttpMessageHandler now makes a clone of the original request, so that the original request can be disposed. This change also makes it possible to assert the content on .NET Framework. From 138f773a41b670ff5572501f541360f9c8005b8c Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sat, 15 Nov 2025 17:03:20 +0100 Subject: [PATCH 05/12] Rewrite tests for WithContent --- .../HttpRequestMessageExtensions.cs | 12 -- .../WithContent.cs | 202 ++++++++++++++++++ .../HasContent.cs | 46 ---- .../HasContentWithPattern.cs | 99 --------- .../WithContent.cs | 68 ------ 5 files changed, 202 insertions(+), 225 deletions(-) create mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithContent.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContent.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentWithPattern.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContent.cs diff --git a/src/TestableHttpClient/HttpRequestMessageExtensions.cs b/src/TestableHttpClient/HttpRequestMessageExtensions.cs index 29bab72..5ba877d 100644 --- a/src/TestableHttpClient/HttpRequestMessageExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessageExtensions.cs @@ -98,18 +98,6 @@ internal static bool HasContentHeader(this HttpRequestMessage httpRequestMessage return httpRequestMessage.Content.Headers.HasHeader(headerName, headerValue); } - /// - /// Determines whether the request has content. - /// - /// A to check for content. - /// true when the request has content; otherwise, false. - internal static bool HasContent(this HttpRequestMessage httpRequestMessage) - { - Guard.ThrowIfNull(httpRequestMessage); - - return httpRequestMessage.Content != null; - } - /// /// Determines whether the request content matches a string pattern. /// diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithContent.cs new file mode 100644 index 0000000..e37ace6 --- /dev/null +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithContent.cs @@ -0,0 +1,202 @@ +namespace TestableHttpClient.Tests.HttpRequestMessageAsserterTests; + +public sealed class WithContent +{ + [Fact] + public void NullContentPattern_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithContent(null!)); + + Assert.Equal("pattern", exception.ParamName); + } + + [Fact] + public void WithNumberOfRequests_NullContentPattern_ShouldThrowArgumentNullException() + { + HttpRequestMessageAsserter sut = new([]); + + var exception = Assert.Throws(() => sut.WithContent(null!, 1)); + + Assert.Equal("pattern", exception.ParamName); + } + + [Fact] + public void EmptyContentPattern_NullContentInRequest_ShouldThrowHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContent("")); + } + + [Fact] + public void WithNumberOfRequests_EmptyContentPattern_NullContentInRequest_ShouldThrowHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContent("", 1)); + } + + [Theory] + [InlineData("")] + [InlineData("Some text")] + [InlineData("{\"key\":\"value\"}")] + public void MatchingContent_DoesNotThrow(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent(content) + }; + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithContent(content); + } + + [Theory] + [InlineData("")] + [InlineData("Some text")] + [InlineData("{\"key\":\"value\"}")] + public void WithMatchingNumberOfRequests_MatchingContent_DoesNotThrow(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent(content) + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithContent(content, 2); + } + + [Theory] + [InlineData("")] + [InlineData("Some text")] + [InlineData("{\"key\":\"value\"}")] + public void WithNotMatchingNumberOfRequests_MatchingContent_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent(content) + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContent(content, 1)); + } + + [Theory] + [InlineData("")] + [InlineData("Some text")] + [InlineData("{\"key\":\"value\"}")] + public void NotMatchingContent_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("Example content") + }; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContent(content)); + } + + [Theory] + [InlineData("")] + [InlineData("Some text")] + [InlineData("{\"key\":\"value\"}")] + public void WithMatchingNumberOfRequests_NotMatchingContent_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("Example content") + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContent(content, 2)); + } + + [Theory] + [InlineData("*")] + [InlineData("username=*&password=*")] + [InlineData("*admin*")] + public void MatchingPattern_DoesNotThrow(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("username=admin&password=admin") + }; + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithContent(content); + } + + [Theory] + [InlineData("*")] + [InlineData("username=*&password=*")] + [InlineData("*admin*")] + public void WithMatchingNumberOfRequests_MatchingPattern_DoesNotThrow(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("username=admin&password=admin") + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithContent(content, 2); + } + + [Theory] + [InlineData("*")] + [InlineData("username=*&password=*")] + [InlineData("*admin*")] + public void WithNotMatchingNumberOfRequests_MatchingPattern_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("username=admin&password=admin") + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContent(content, 1)); + } + + [Theory] + [InlineData("admin")] + [InlineData("*test*")] + public void NotMatchingPattern_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("username=admin&password=admin") + }; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContent(content)); + } + + [Theory] + [InlineData("admin")] + [InlineData("*test*")] + public void WithMatchingNumberOfRequests_NotMatchingPattern_ThrowsHttpRequestMessageAssertionException(string content) + { + using HttpRequestMessage request = new() + { + Content = new StringContent("username=admin&password=admin") + }; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContent(content, 2)); + } +} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContent.cs deleted file mode 100644 index 018ced0..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContent.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - public void HasContent_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasContent()); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasContent_NullContent_ReturnsFalse() - { - using HttpRequestMessage sut = new(); - - Assert.False(sut.HasContent()); - } - - [Fact] - public void HasContent_NotNullContent_ReturnsTrue() - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - - Assert.True(sut.HasContent()); - } - - [Fact] - public void HasContent_DisposedContent_ReturnsTrue() - { - StringContent content = new(""); - content.Dispose(); - - using HttpRequestMessage sut = new() - { - Content = content - }; - - Assert.True(sut.HasContent()); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentWithPattern.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentWithPattern.cs deleted file mode 100644 index 87f8dce..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentWithPattern.cs +++ /dev/null @@ -1,99 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - public void HasContentWithPattern_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasContent("")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - public void HasContentWithPattern_NullExpectedContent_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - - var exception = Assert.Throws(() => sut.HasContent(null!)); - Assert.Equal("pattern", exception.ParamName); - } - - [Fact] - public void HasContentWithPattern_NoContent_ReturnsFalse() - { - using HttpRequestMessage sut = new(); - - Assert.False(sut.HasContent("")); - } - - [Theory] - [InlineData("")] - [InlineData("Some text")] - [InlineData("{\"key\":\"value\"}")] - public void HasContentWithPattern_ExactlyMatchingStringContent_ReturnsTrue(string content) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent(content) - }; - - Assert.True(sut.HasContent(content)); - } - - [Theory] - [InlineData("")] - [InlineData("Some text")] - [InlineData("{\"key\":\"value\"}")] - public void HasContentWithPattern_NotMatchingStringContent_ReturnsFalse(string content) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("Example content") - }; - - Assert.False(sut.HasContent(content)); - } - - [Theory] - [InlineData("*")] - [InlineData("username=*&password=*")] - [InlineData("*admin*")] - public void HasContentWithPattern_MatchingPattern_ReturnsTrue(string pattern) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("username=admin&password=admin") - }; - - Assert.True(sut.HasContent(pattern)); - } - - [Theory] - [InlineData("admin")] - [InlineData("*test*")] - public void HasContentWithPattern_NotMatchingPattern_ReturnsFalse(string pattern) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("username=admin&password=admin") - }; - - Assert.False(sut.HasContent(pattern)); - } - - [Fact] - public void HasContentWithPattern_DisposedContent_ThrowsObjectDisposedException() - { - StringContent content = new(""); - content.Dispose(); - - using HttpRequestMessage sut = new() - { - Content = content - }; - - Assert.Throws(() => sut.HasContent("*")); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContent.cs deleted file mode 100644 index bfe59fa..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContent.cs +++ /dev/null @@ -1,68 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -public class WithContent -{ - [Fact] - public void WithContent_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithContent("*")); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithContent_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = null!; - - var exception = Assert.Throws(() => sut.WithContent("*", 1)); - - Assert.Equal("check", exception.ParamName); - } - - [Fact] - public void WithContent_WithoutNumberOfRequests_NullPattern_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithContent(null!)); - - Assert.Equal("pattern", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithContent_WithNumberOfRequests_NullPattern_ThrowsArgumentNullException() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - var exception = Assert.Throws(() => sut.WithContent(null!, 1)); - - Assert.Equal("pattern", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); - } - - [Fact] - public void WithContent_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithContent("some content"); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "content 'some content'"); - } - - [Fact] - public void WithContent_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithContent("some content", 1); - - sut.Received().WithFilter(Args.AnyPredicate(), (int?)1, "content 'some content'"); - } -} From b9fd0961822e045832ec4ab1a7f8d3fb5b774b6e Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sat, 15 Nov 2025 17:12:59 +0100 Subject: [PATCH 06/12] Small async improvements --- src/TestableHttpClient/HttpRequestMessageExtensions.cs | 5 ++++- src/TestableHttpClient/Response/TimeoutResponse.cs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/TestableHttpClient/HttpRequestMessageExtensions.cs b/src/TestableHttpClient/HttpRequestMessageExtensions.cs index 5ba877d..26abc9a 100644 --- a/src/TestableHttpClient/HttpRequestMessageExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessageExtensions.cs @@ -114,7 +114,10 @@ internal static bool HasContent(this HttpRequestMessage httpRequestMessage, stri return false; } - var stringContent = httpRequestMessage.Content.ReadAsStringAsync().Result; + var stringContent = httpRequestMessage.Content.ReadAsStringAsync() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); return pattern switch { diff --git a/src/TestableHttpClient/Response/TimeoutResponse.cs b/src/TestableHttpClient/Response/TimeoutResponse.cs index e8772f6..114d475 100644 --- a/src/TestableHttpClient/Response/TimeoutResponse.cs +++ b/src/TestableHttpClient/Response/TimeoutResponse.cs @@ -10,9 +10,9 @@ public async Task ExecuteAsync(HttpResponseContext context, CancellationToken ca { #if NETSTANDARD cancelationSource.Cancel(false); - await Task.FromCanceled(cancellationToken).ConfigureAwait(true); + await Task.FromCanceled(cancellationToken).ConfigureAwait(false); #else - await cancelationSource.CancelAsync().ConfigureAwait(true); + await cancelationSource.CancelAsync().ConfigureAwait(false); #endif } From d635e8a2d07a39180801705522fa424f6e658a38 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 11:58:56 +0100 Subject: [PATCH 07/12] Refactor some more tests to follow same pattern --- .../HasContentHeader.cs | 182 ------------------ .../HasRequestHeader.cs | 137 ------------- .../Args.cs | 9 - .../WithContentHeaderName.cs | 84 ++++++-- .../WithContentHeaderNameAndValue.cs | 139 ++++++++++--- .../WithFormUrlEncodedContent.cs | 32 ++- .../WithJsonContent.cs | 59 +----- .../WithRequestHeaderName.cs | 69 ++++--- .../WithRequestHeaderNameAndValue.cs | 133 +++++++++---- 9 files changed, 330 insertions(+), 514 deletions(-) delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs delete mode 100644 test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/Args.cs diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs deleted file mode 100644 index 7ab243d..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasContentHeader.cs +++ /dev/null @@ -1,182 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasContentHeader("Content-Disposition")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NullHeaderName_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - - var exception = Assert.Throws(() => sut.HasContentHeader(null!)); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_EmptyHeaderName_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - - var exception = Assert.Throws(() => sut.HasContentHeader(string.Empty)); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasContentHeader("Content-Disposition", "inline")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasContentHeader(null!, "inline")); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasContentHeader(string.Empty, "inline")); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasContentHeader("Content-Disposition", null!)); - Assert.Equal("headerValue", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasContentHeader("Content-Disposition", string.Empty)); - Assert.Equal("headerValue", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_ExistingHeaderName_ReturnsTrue() - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - sut.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline"); - - Assert.True(sut.HasContentHeader("Content-Disposition")); - } - - [Theory] - [InlineData("Host")] - [InlineData("Content-Disposition")] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NotExistingHeaderName_ReturnsFalse(string headerName) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - - Assert.False(sut.HasContentHeader(headerName)); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NoContent_ReturnsFalse() - { - using HttpRequestMessage sut = new() - { - Content = null - }; - - Assert.False(sut.HasContentHeader("Content-Disposition")); - } - - [Theory] - [InlineData("inline; filename=empty.file")] - [InlineData("inline; *")] - [InlineData("*; filename=empty.file")] - [InlineData("*")] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string value) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - sut.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline") - { - FileName = "empty.file" - }; - - Assert.True(sut.HasContentHeader("Content-Disposition", value)); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NotExitingHeaderNameAndValue_ReturnsFalse() - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - - Assert.False(sut.HasContentHeader("Host", "inline")); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_NullContentNotExitingHeaderNameAndValue_ReturnsFalse() - { - using HttpRequestMessage sut = new() - { - Content = null - }; - - Assert.False(sut.HasContentHeader("Host", "inline")); - } - - [Theory] - [InlineData("inline; filename=emtpy.file")] - [InlineData("inline; *")] - [InlineData("*; filename=empty.file")] - [Obsolete("Use HasHeader instead.")] - public void HasContentHeader_ExistingHeaderNameNotMatchingValue_ReturnsFalse(string value) - { - using HttpRequestMessage sut = new() - { - Content = new StringContent("") - }; - sut.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") - { - FileName = "attachment.file" - }; - - Assert.False(sut.HasContentHeader("Content-Disposition", value)); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs b/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs deleted file mode 100644 index b567504..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessageExtensionsTests/HasRequestHeader.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace TestableHttpClient.Tests; - -public partial class HttpRequestMessageExtensionsTests -{ - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NullRequest_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasRequestHeader("host")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NullHeaderName_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - - var exception = Assert.Throws(() => sut.HasRequestHeader(null!)); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_EmptyHeaderName_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - - var exception = Assert.Throws(() => sut.HasRequestHeader(string.Empty)); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NullRequestNonNullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() - { - HttpRequestMessage sut = null!; - - var exception = Assert.Throws(() => sut.HasRequestHeader("host", "value")); - Assert.Equal("httpRequestMessage", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NullHeaderNameAndNonNullHeaderValue_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasRequestHeader(null!, "value")); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_EmptyHeaderNameAndNonNullHeaderValue_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasRequestHeader(string.Empty, "value")); - Assert.Equal("headerName", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NonNullHeaderNameAndNullHeaderValue_ThrowsArgumentNullException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasRequestHeader("Host", null!)); - Assert.Equal("headerValue", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NonNullHeaderNameAndEmptyHeaderValue_ThrowsArgumentException() - { - using HttpRequestMessage sut = new(); - var exception = Assert.Throws(() => sut.HasRequestHeader("Host", string.Empty)); - Assert.Equal("headerValue", exception.ParamName); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_ExistingHeaderName_ReturnsTrue() - { - using HttpRequestMessage sut = new(); - sut.Headers.Host = "example.com"; - - Assert.True(sut.HasRequestHeader("Host")); - } - - [Theory] - [InlineData("Host")] - [InlineData("Content-Type")] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NotExistingHeaderName_ReturnsFalse(string headerName) - { - using HttpRequestMessage sut = new(); - - Assert.False(sut.HasRequestHeader(headerName)); - } - - [Theory] - [InlineData("example.com")] - [InlineData("example*")] - [InlineData("*.com")] - [InlineData("*")] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_ExistingHeaderNameMatchingValue_ReturnsTrue(string value) - { - using HttpRequestMessage sut = new(); - sut.Headers.Host = "example.com"; - - Assert.True(sut.HasRequestHeader("Host", value)); - } - - [Fact] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_NotExitingHeaderNameAndValue_ReturnsFalse() - { - using HttpRequestMessage sut = new(); - - Assert.False(sut.HasRequestHeader("Content-Type")); - } - - [Theory] - [InlineData("example.com")] - [InlineData("example*")] - [InlineData("*.com")] - [Obsolete("Use HasHeader instead.")] - public void HasRequestHeader_ExistingHeaderNameNotMatchingValue_ReturnsFalse(string value) - { - using HttpRequestMessage sut = new(); - sut.Headers.Host = "myhost.net"; - - Assert.False(sut.HasRequestHeader("Host", value)); - } -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/Args.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/Args.cs deleted file mode 100644 index e23bde5..0000000 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/Args.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; - -internal static class Args -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Non-substitutable member", "NS1004:Argument matcher used with a non-virtual member of a class.", Justification = "This is a custom matcher.")] - public static ref Func AnyPredicate() => ref Arg.Any>(); -} diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs index 4bf4191..99cd06e 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderName.cs @@ -1,6 +1,4 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; [Obsolete("Use WithHeader")] public class WithContentHeaderName @@ -8,7 +6,7 @@ public class WithContentHeaderName [Fact] public void WithContentHeader_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithContentHeader("someHeader")); @@ -18,7 +16,7 @@ public void WithContentHeader_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNu [Fact] public void WithContentHeader_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", 1)); @@ -28,64 +26,112 @@ public void WithContentHeader_WithNumberOfRequests_NullCheck_ThrowsArgumentNullE [Fact] public void WithContentHeader_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(null!)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeader_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(string.Empty)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeader_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(null!, 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeader_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(string.Empty, 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] - public void WithContentHeader_WithoutNumberOfRequests_CallsWithCorrectly() + public void WithMatchingContentHeader_WithoutNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + HttpRequestMessageAsserter sut = new([request]); sut.WithContentHeader("Content-Type"); + } + + [Fact] + public void WithNoContent_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); - sut.Received().WithFilter(Args.AnyPredicate(), null, "content header 'Content-Type'"); + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Type")); + } + + [Fact] + public void WithNotMatchingContentHeader_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Disposition")); } [Fact] - public void WithContentHeader_WithNumberOfRequests_CallsWithCorrectly() + public void WithContentHeader_WithNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + HttpRequestMessageAsserter sut = new([request]); sut.WithContentHeader("Content-Type", 1); + } + + [Fact] + public void WithNoContent_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + + HttpRequestMessageAsserter sut = new([request, request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "content header 'Content-Type'"); + Assert.Throws(() => sut.WithContentHeader("Content-Type", 2)); + } + + [Fact] + public void WithNotMatchingContentHeader_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new() + { + Content = new StringContent("") + }; + request.Content.Headers.ContentType = new("application/json"); + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Disposition", 2)); } } diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs index bfb8b02..1cca302 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithContentHeaderNameAndValue.cs @@ -1,14 +1,12 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; [Obsolete("Use WithHeader")] public class WithContentHeaderNameAndValue { [Fact] - public void WihtContentHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() + public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", "someValue")); @@ -16,9 +14,9 @@ public void WihtContentHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_Thro } [Fact] - public void WihtContentHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() + public void WithContentHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", "someValue", 1)); @@ -28,108 +26,187 @@ public void WihtContentHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsA [Fact] public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(null!, "someValue")); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(string.Empty, "someValue")); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(null!, "someValue", 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader(string.Empty, "someValue", 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_NullValue_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", null!)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_EmptyValue_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", string.Empty)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithNumberOfRequests_NullValue_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", null!, 1)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithContentHeaderNameAndValue_WithNumberOfRequests_EmptyValue_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithContentHeader("someHeader", string.Empty, 1)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); + } + + [Theory] + [InlineData("*")] + [InlineData("application/*")] + [InlineData("application/json*")] + [InlineData("application/json; charset=utf-8")] + public void WithMatchingContentHeaderNameAndValue_WithoutNumberOfRequests_DoesNotThrow(string headerValue) + { + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithContentHeader("Content-Type", headerValue); + } + + [Theory] + [InlineData("*")] + [InlineData("application/*")] + [InlineData("application/json*")] + [InlineData("application/json; charset=utf-8")] + public void WithContentHeaderNameAndValue_WithNumberOfRequests_DoesNotThrow(string headerValue) + { + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithContentHeader("Content-Type", headerValue, 2); + } + + [Fact] + public void WithNoContent_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Content = null; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Type", "application/json*")); + } + + [Fact] + public void WithNoContent_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Content = null; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Type", "application/json*", 2)); + } + + [Fact] + public void WithNotMatchingContentHeaderNameAndMatchingValue_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContentHeader("Host", "application/json*")); + } + + [Fact] + public void WithNotMatchingContentHeaderNameAndMatchingValue_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithContentHeader("Host", "application/json*", 2)); + } + + [Fact] + public void WithMatchingContentHeaderNameAndNotMatchingValue_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithContentHeader("Content-Type", "text/yaml*")); } [Fact] - public void WithContentHeaderNameAndValue_WithoutNumberOfRequests_CallsWithCorrectly() + public void WithMatchingContentHeaderNameAndNotMatchingValue_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); - sut.WithContentHeader("someHeader", "someValue"); + HttpRequestMessageAsserter sut = new([request, request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "content header 'someHeader' and value 'someValue'"); + Assert.Throws(() => sut.WithContentHeader("Content-Type", "text/yaml*", 2)); } [Fact] - public void WithContentHeaderNameAndValue_WithNumberOfRequests_CallsWithCorrectly() + public void WithMatchingContentHeaderNameAndMatchingValue_WithNotMatchingNumberOfRequests_ThrowsHttpRequestMessageAssertionException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Content = new StringContent("", Encoding.UTF8, "application/json"); - sut.WithContentHeader("someHeader", "someValue", 1); + HttpRequestMessageAsserter sut = new([request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "content header 'someHeader' and value 'someValue'"); + Assert.Throws(() => sut.WithContentHeader("Content-Type", "application/json*", 2)); } } diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs index 83c903f..d147730 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs @@ -1,13 +1,11 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; public class WithFormUrlEncodedContent { [Fact] public void WithFormUrlEncodedContent_WithoutNumberOfRequests_NulCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent([])); @@ -17,7 +15,7 @@ public void WithFormUrlEncodedContent_WithoutNumberOfRequests_NulCheck_ThrowsArg [Fact] public void WithFormUrlEncodedContent_WithNumberOfRequests_NulCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent([], 1)); @@ -27,43 +25,41 @@ public void WithFormUrlEncodedContent_WithNumberOfRequests_NulCheck_ThrowsArgume [Fact] public void WithFormUrlEncodedContent_WithoutNumberOfRequests_NullNameValueCollection_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent(null!)); Assert.Equal("nameValueCollection", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithFormUrlEncodedContent_WithNumberOfRequests_NullNameValueCollection_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent(null!, 1)); Assert.Equal("nameValueCollection", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] - public void WithFormUrlEncodedContent_WithoutNumberOfRequests_CallsWithCorrectly() + public void WithFormMatchingUrlEncodedContent_WithoutNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Content = new FormUrlEncodedContent([new KeyValuePair("username", "alice")]); + HttpRequestMessageAsserter sut = new([request]); sut.WithFormUrlEncodedContent([new KeyValuePair("username", "alice")]); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "form url encoded content 'username=alice'"); } [Fact] - public void WithFormUrlEncodedContent_WithNumberOfRequests_CallsWithCorrectly() + public void WithMatchingFormUrlEncodedContent_WithNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); - - sut.WithFormUrlEncodedContent([new KeyValuePair("username", "alice")], 1); + using HttpRequestMessage request = new(); + request.Content = new FormUrlEncodedContent([new KeyValuePair("username", "alice")]); + HttpRequestMessageAsserter sut = new([request, request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "form url encoded content 'username=alice'"); + sut.WithFormUrlEncodedContent([new KeyValuePair("username", "alice")], 2); } [Fact] diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs index c737488..8d9fa57 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs @@ -1,15 +1,11 @@ -using System.Text.Json; - -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; public class WithJsonContent { [Fact] public void WithJsonContent_WithoutNumberOfRequests_NullChecker_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithJsonContent(null)); @@ -19,62 +15,13 @@ public void WithJsonContent_WithoutNumberOfRequests_NullChecker_ThrowsArgumentNu [Fact] public void WithJsonContent_WithNumberOfRequests_NullChecker_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; var exception = Assert.Throws(() => sut.WithJsonContent(null, 1)); Assert.Equal("check", exception.ParamName); } - [Fact] - public void WithJsonContent_WithoutNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - sut.Options.Returns(new TestableHttpMessageHandlerOptions()); - - sut.WithJsonContent(null); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "json content 'null'"); - _ = sut.Received(1).Options; - } - - [Fact] - public void WithJsonContent_WithoutNumberOfRequestsWithCustomJsonSerializerOptions_DoesNotCallOptionsFromCheck() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - sut.Options.Returns(new TestableHttpMessageHandlerOptions()); - - sut.WithJsonContent(null, new JsonSerializerOptions()); - - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "json content 'null'"); - _ = sut.DidNotReceive().Options; - } - - [Fact] - public void WithJsonContent_WithNumberOfRequests_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - sut.Options.Returns(new TestableHttpMessageHandlerOptions()); - - sut.WithJsonContent(null, 1); - - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "json content 'null'"); - _ = sut.Received(1).Options; - } - - - [Fact] - public void WithJsonContent_WithNumberOfRequestsAndJsonSerializerOptions_CallsWithCorrectly() - { - IHttpRequestMessagesCheck sut = Substitute.For(); - sut.Options.Returns(new TestableHttpMessageHandlerOptions()); - - sut.WithJsonContent(null, new JsonSerializerOptions(), 1); - - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "json content 'null'"); - _ = sut.DidNotReceive().Options; - } - [Fact] public void WithJsonContent_RequestWithMatchingContent_ReturnsHttpRequestMessageAsserter() { diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs index 649b7c1..5ef3e7f 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderName.cs @@ -1,6 +1,4 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; [Obsolete("Use WithHeader")] public class WithRequestHeaderName @@ -8,9 +6,9 @@ public class WithRequestHeaderName [Fact] public void WithRequestHeader_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader")); + var exception = Assert.Throws(() => sut.WithRequestHeader("host")); Assert.Equal("check", exception.ParamName); } @@ -18,9 +16,9 @@ public void WithRequestHeader_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNu [Fact] public void WithRequestHeader_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", 1)); Assert.Equal("check", exception.ParamName); } @@ -28,64 +26,89 @@ public void WithRequestHeader_WithNumberOfRequests_NullCheck_ThrowsArgumentNullE [Fact] public void WithRequestHeader_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithRequestHeader(null!)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeader_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeader_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithRequestHeader(null!, 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeader_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty, 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] - public void WithRequestHeader_WithoutNumberOfRequests_CallsWithCorrectly() + public void WithMatchingRequestHeader_WithoutNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + HttpRequestMessageAsserter sut = new([request]); + + sut.WithRequestHeader("Host"); + } - sut.WithRequestHeader("api-version"); + [Fact] + public void WithNotMatchingRequestHeader_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + HttpRequestMessageAsserter sut = new([request]); - sut.Received().WithFilter(Args.AnyPredicate(), null, "request header 'api-version'"); + Assert.Throws(() => sut.WithRequestHeader("host")); } [Fact] - public void WithRequestHeader_WithNumberOfRequests_CallsWithCorrectly() + public void WithRequestHeader_WithNumberOfRequests_DoesNotThrow() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithRequestHeader("Host", 2); + } - sut.WithRequestHeader("api-version", 1); + [Fact] + public void WithNotMatchingRequestHeader_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithRequestHeader("Host", 2)); + } + + [Fact] + public void WithMatchingRequestHeader_WithNotMatchingNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + HttpRequestMessageAsserter sut = new([request]); - sut.Received().WithFilter(Args.AnyPredicate(), (int?)1, "request header 'api-version'"); + Assert.Throws(() => sut.WithRequestHeader("Host", 2)); } } diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs index e76511a..d920e12 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithRequestHeaderNameAndValue.cs @@ -1,6 +1,4 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; +namespace TestableHttpClient.Tests.HttpRequestMessagesCheckExtensionsTests; [Obsolete("Use WithHeader")] public class WithRequestHeaderNameAndValue @@ -8,9 +6,9 @@ public class WithRequestHeaderNameAndValue [Fact] public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", "someValue")); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", "example.com")); Assert.Equal("check", exception.ParamName); } @@ -18,9 +16,9 @@ public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_NullCheck_Thro [Fact] public void WithRequestHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = null!; + HttpRequestMessageAsserter sut = null!; - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", "someValue", 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", "example.com", 1)); Assert.Equal("check", exception.ParamName); } @@ -28,108 +26,165 @@ public void WithRequestHeaderNameAndValue_WithNumberOfRequests_NullCheck_ThrowsA [Fact] public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader(null!, "someValue")); + var exception = Assert.Throws(() => sut.WithRequestHeader(null!, "example.com")); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty, "someValue")); + var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty, "example.com")); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithNumberOfRequests_NullHeaderName_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader(null!, "someValue", 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader(null!, "example.com", 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithNumberOfRequests_EmptyHeaderName_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty, "someValue", 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader(string.Empty, "example.com", 1)); Assert.Equal("headerName", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_NullValue_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", null!)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", null!)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_EmptyValue_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", string.Empty)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", string.Empty)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithNumberOfRequests_NullValue_ThrowsArgumentNullException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", null!, 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", null!, 1)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); } [Fact] public void WithRequestHeaderNameAndValue_WithNumberOfRequests_EmptyValue_ThrowsArgumentException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + HttpRequestMessageAsserter sut = new([]); - var exception = Assert.Throws(() => sut.WithRequestHeader("someHeader", string.Empty, 1)); + var exception = Assert.Throws(() => sut.WithRequestHeader("host", string.Empty, 1)); Assert.Equal("headerValue", exception.ParamName); - sut.DidNotReceive().WithFilter(Args.AnyPredicate(), Arg.Any(), Arg.Any()); + } + + [Theory] + [InlineData("*")] + [InlineData("*.com")] + [InlineData("example*")] + [InlineData("example.com")] + public void WithMatchingRequestHeaderNameAndValue_WithoutNumberOfRequests_DoesNotThrow(string headerValue) + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request]); + + sut.WithRequestHeader("host", headerValue); + } + + [Theory] + [InlineData("*")] + [InlineData("*.com")] + [InlineData("example*")] + [InlineData("example.com")] + public void WithRequestHeaderNameAndValue_WithNumberOfRequests_DoesNotThrow(string headerValue) + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request, request]); + + sut.WithRequestHeader("host", headerValue, 2); + } + + [Fact] + public void WithNotMatchingRequestHeaderNameAndMatchingValue_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithRequestHeader("Via", "example.com")); + } + + [Fact] + public void WithNotMatchingRequestHeaderNameAndMatchingValue_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request, request]); + + Assert.Throws(() => sut.WithRequestHeader("Via", "example.com", 2)); + } + + [Fact] + public void WithMatchingRequestHeaderNameAndNotMatchingValue_WithoutNumberOfRequests_ThrowsHttpRequestMessageAssertionException() + { + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; + + HttpRequestMessageAsserter sut = new([request]); + + Assert.Throws(() => sut.WithRequestHeader("Host", "text/yaml*")); } [Fact] - public void WithRequestHeaderNameAndValue_WithoutNumberOfRequests_CallsWithCorrectly() + public void WithMatchingRequestHeaderNameAndNotMatchingValue_WithNumberOfRequests_ThrowsHttpRequestMessageAssertionException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; - sut.WithRequestHeader("someHeader", "someValue"); + HttpRequestMessageAsserter sut = new([request, request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), null, "request header 'someHeader' and value 'someValue'"); + Assert.Throws(() => sut.WithRequestHeader("Host", "text/yaml*", 2)); } [Fact] - public void WithRequestHeaderNameAndValue_WithNumberOfRequests_CallsWithCorrectly() + public void WithMatchingRequestHeaderNameAndMatchingValue_WithNotMatchingNumberOfRequests_ThrowsHttpRequestMessageAssertionException() { - IHttpRequestMessagesCheck sut = Substitute.For(); + using HttpRequestMessage request = new(); + request.Headers.Host = "example.com"; - sut.WithRequestHeader("someHeader", "someValue", 1); + HttpRequestMessageAsserter sut = new([request]); - sut.Received(1).WithFilter(Args.AnyPredicate(), (int?)1, "request header 'someHeader' and value 'someValue'"); + Assert.Throws(() => sut.WithRequestHeader("Host", "example.com", 2)); } } From b0a987dead6116db321038bf4851ba1f0f612ea2 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 12:31:10 +0100 Subject: [PATCH 08/12] Move several HttpRequestMessagesCheckExtensions to HttpRequestMessageAsserter and specify them on IHttpRequestMessagesCheck interface --- CHANGELOG.md | 1 + .../HttpRequestMessageAsserter.cs | 144 ++++++++++++++++ .../HttpRequestMessagesCheckExtensions.cs | 161 ------------------ .../IHttpRequestMessagesCheck.cs | 92 ++++++++++ src/TestableHttpClient/PublicAPI.Shipped.txt | 12 -- .../PublicAPI.Unshipped.txt | 14 +- 6 files changed, 250 insertions(+), 174 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a584a5..335c4a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - The TestableHttpMessageHandler now makes a clone of the original request, so that the original request can be disposed. This change also makes it possible to assert the content on .NET Framework. +- Moved `WithHttpMethod`, `WithRequestUri`, `WithHttpVersion`, `WithHeader` and `WithContent` from `HttpRequestMessagesCheckExtensions` to `HttpRequestMessageAsserter` and specify them on the `IHttpRequestMessagesCheck` interface. ## [0.11] - 2024-06-15 ### Removed diff --git a/src/TestableHttpClient/HttpRequestMessageAsserter.cs b/src/TestableHttpClient/HttpRequestMessageAsserter.cs index 251ae1e..3559981 100644 --- a/src/TestableHttpClient/HttpRequestMessageAsserter.cs +++ b/src/TestableHttpClient/HttpRequestMessageAsserter.cs @@ -81,4 +81,148 @@ public IHttpRequestMessagesCheck WithFilter(Func reque } return this; } + + /// + /// Asserts whether requests were made to a given URI based on a pattern. + /// + /// The uri pattern that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithRequestUri(string pattern) => WithRequestUri(pattern, null); + + /// + /// Asserts whether requests were made to a given URI based on a pattern. + /// + /// The uri pattern that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithRequestUri(string pattern, int expectedNumberOfRequests) => WithRequestUri(pattern, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithRequestUri(string pattern, int? expectedNumberOfRequests) + { + Guard.ThrowIfNullOrEmpty(pattern); + + var condition = string.Empty; + if (pattern != "*") + { + condition = $"uri pattern '{pattern}'"; + } + + UriPattern uriPattern = UriPatternParser.Parse(pattern); + + return WithFilter(x => x.RequestUri is not null && uriPattern.Matches(x.RequestUri, Options.UriPatternMatchingOptions), expectedNumberOfRequests, condition); + } + + /// + /// Asserts whether requests were made with a given HTTP Method. + /// + /// The that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod) => WithHttpMethod(httpMethod, null); + + /// + /// Asserts whether requests were made with a given HTTP Method. + /// + /// The that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod, int expectedNumberOfRequests) => WithHttpMethod(httpMethod, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod, int? expectedNumberOfRequests) + { + Guard.ThrowIfNull(httpMethod); + + return WithFilter(x => x.HasHttpMethod(httpMethod), expectedNumberOfRequests, $"HTTP Method '{httpMethod}'"); + } + + /// + /// Asserts whether requests were made using a specific HTTP Version. + /// + /// The implementation that hold all the request messages. + /// The that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion) => WithHttpVersion(httpVersion, null); + + /// + /// Asserts whether requests were made using a specific HTTP Version. + /// + /// The that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion, int expectedNumberOfRequests) => WithHttpVersion(httpVersion, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion, int? expectedNumberOfRequests) + { + Guard.ThrowIfNull(httpVersion); + + return WithFilter(x => x.HasHttpVersion(httpVersion), expectedNumberOfRequests, $"HTTP Version '{httpVersion}'"); + } + + /// + /// Asserts whether requests were made with a specific header name. Values are ignored. + /// + /// The name of the header that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName) => WithHeader(headerName, (int?)null); + + /// + /// Asserts whether requests were made with a specific header name. Values are ignored. + /// + /// The name of the header that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, int expectedNumberOfRequests) => WithHeader(headerName, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithHeader(string headerName, int? expectedNumberOfRequests) + { + Guard.ThrowIfNullOrEmpty(headerName); + + return WithFilter(x => x.HasHeader(headerName), expectedNumberOfRequests, $"header '{headerName}'"); + } + + /// + /// Asserts whether requests were made with a specific header name and value. + /// + /// The name of the header that is expected. + /// The value of the expected header, supports wildcards. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue) => WithHeader(headerName, headerValue, null); + + /// + /// Asserts whether requests were made with a specific header name and value. + /// + /// The name of the header that is expected. + /// The value of the expected header, supports wildcards. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue, int expectedNumberOfRequests) => WithHeader(headerName, headerValue, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue, int? expectedNumberOfRequests) + { + Guard.ThrowIfNullOrEmpty(headerName); + Guard.ThrowIfNullOrEmpty(headerValue); + + return WithFilter(x => x.HasHeader(headerName, headerValue), expectedNumberOfRequests, $"header '{headerName}' and value '{headerValue}'"); + } + + /// + /// Asserts whether requests were made with specific content. + /// + /// The expected content, supports wildcards. + /// The for further assertions. + public IHttpRequestMessagesCheck WithContent(string pattern) => WithContent(pattern, null); + + /// + /// Asserts whether requests were made with specific content. + /// + /// The expected content, supports wildcards. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithContent(string pattern, int expectedNumberOfRequests) => WithContent(pattern, (int?)expectedNumberOfRequests); + + private IHttpRequestMessagesCheck WithContent(string pattern, int? expectedNumberOfRequests) + { + Guard.ThrowIfNull(pattern); + + return WithFilter(x => x.HasContent(pattern), expectedNumberOfRequests, $"content '{pattern}'"); + } } diff --git a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs index fff199c..a0c6cd6 100644 --- a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs @@ -2,89 +2,6 @@ public static class HttpRequestMessagesCheckExtensions { - /// - /// Asserts whether requests were made to a given URI based on a pattern. - /// - /// The implementation that hold all the request messages. - /// The uri pattern that is expected. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithRequestUri(this IHttpRequestMessagesCheck check, string pattern) => WithRequestUri(check, pattern, null); - - /// - /// Asserts whether requests were made to a given URI based on a pattern. - /// - /// The implementation that hold all the request messages. - /// The uri pattern that is expected. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithRequestUri(this IHttpRequestMessagesCheck check, string pattern, int expectedNumberOfRequests) => WithRequestUri(check, pattern, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithRequestUri(this IHttpRequestMessagesCheck check, string pattern, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(pattern); - - var condition = string.Empty; - if (pattern != "*") - { - condition = $"uri pattern '{pattern}'"; - } - - UriPattern uriPattern = UriPatternParser.Parse(pattern); - - return check.WithFilter(x => x.RequestUri is not null && uriPattern.Matches(x.RequestUri, check.Options.UriPatternMatchingOptions), expectedNumberOfRequests, condition); - } - - /// - /// Asserts whether requests were made with a given HTTP Method. - /// - /// The implementation that hold all the request messages. - /// The that is expected. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHttpMethod(this IHttpRequestMessagesCheck check, HttpMethod httpMethod) => WithHttpMethod(check, httpMethod, null); - - /// - /// Asserts whether requests were made with a given HTTP Method. - /// - /// The implementation that hold all the request messages. - /// The that is expected. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHttpMethod(this IHttpRequestMessagesCheck check, HttpMethod httpMethod, int expectedNumberOfRequests) => WithHttpMethod(check, httpMethod, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithHttpMethod(this IHttpRequestMessagesCheck check, HttpMethod httpMethod, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNull(httpMethod); - - return check.WithFilter(x => x.HasHttpMethod(httpMethod), expectedNumberOfRequests, $"HTTP Method '{httpMethod}'"); - } - - /// - /// Asserts whether requests were made using a specific HTTP Version. - /// - /// The implementation that hold all the request messages. - /// The that is expected. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHttpVersion(this IHttpRequestMessagesCheck check, Version httpVersion) => WithHttpVersion(check, httpVersion, null); - - /// - /// Asserts whether requests were made using a specific HTTP Version. - /// - /// The implementation that hold all the request messages. - /// The that is expected. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHttpVersion(this IHttpRequestMessagesCheck check, Version httpVersion, int expectedNumberOfRequests) => WithHttpVersion(check, httpVersion, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithHttpVersion(this IHttpRequestMessagesCheck check, Version httpVersion, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNull(httpVersion); - - return check.WithFilter(x => x.HasHttpVersion(httpVersion), expectedNumberOfRequests, $"HTTP Version '{httpVersion}'"); - } - /// /// Asserts whether requests were made with a specific header name. Values are ignored. /// @@ -211,84 +128,6 @@ private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMess return check.WithFilter(x => x.HasContentHeader(headerName, headerValue), expectedNumberOfRequests, $"content header '{headerName}' and value '{headerValue}'"); } - /// - /// Asserts whether requests were made with a specific header name. Values are ignored. - /// - /// The implementation that hold all the request messages. - /// The name of the header that is expected. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName) => WithHeader(check, headerName, (int?)null); - - /// - /// Asserts whether requests were made with a specific header name. Values are ignored. - /// - /// The implementation that hold all the request messages. - /// The name of the header that is expected. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) => WithHeader(check, headerName, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - - return check.WithFilter(x => x.HasHeader(headerName), expectedNumberOfRequests, $"header '{headerName}'"); - } - - /// - /// Asserts whether requests were made with a specific header name and value. - /// - /// The implementation that hold all the request messages. - /// The name of the header that is expected. - /// The value of the expected header, supports wildcards. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) => WithHeader(check, headerName, headerValue, null); - - /// - /// Asserts whether requests were made with a specific header name and value. - /// - /// The implementation that hold all the request messages. - /// The name of the header that is expected. - /// The value of the expected header, supports wildcards. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) => WithHeader(check, headerName, headerValue, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - Guard.ThrowIfNullOrEmpty(headerValue); - - return check.WithFilter(x => x.HasHeader(headerName, headerValue), expectedNumberOfRequests, $"header '{headerName}' and value '{headerValue}'"); - } - - /// - /// Asserts whether requests were made with specific content. - /// - /// The implementation that hold all the request messages. - /// The expected content, supports wildcards. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithContent(this IHttpRequestMessagesCheck check, string pattern) => WithContent(check, pattern, null); - - /// - /// Asserts whether requests were made with specific content. - /// - /// The implementation that hold all the request messages. - /// The expected content, supports wildcards. - /// The expected number of requests. - /// The for further assertions. - public static IHttpRequestMessagesCheck WithContent(this IHttpRequestMessagesCheck check, string pattern, int expectedNumberOfRequests) => WithContent(check, pattern, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithContent(this IHttpRequestMessagesCheck check, string pattern, int? expectedNumberOfRequests) - { - Guard.ThrowIfNull(check); - Guard.ThrowIfNull(pattern); - - return check.WithFilter(x => x.HasContent(pattern), expectedNumberOfRequests, $"content '{pattern}'"); - } - /// /// Asserts whether requests are made with specific json content. /// diff --git a/src/TestableHttpClient/IHttpRequestMessagesCheck.cs b/src/TestableHttpClient/IHttpRequestMessagesCheck.cs index d8faf81..9fcd12f 100644 --- a/src/TestableHttpClient/IHttpRequestMessagesCheck.cs +++ b/src/TestableHttpClient/IHttpRequestMessagesCheck.cs @@ -35,4 +35,96 @@ public interface IHttpRequestMessagesCheck /// The name of the condition, used in the exception message. /// The for further assertions. public IHttpRequestMessagesCheck WithFilter(Func requestFilter, int? expectedNumberOfRequests, string condition); + + /// + /// Asserts whether requests were made with a given HTTP Method. + /// + /// The that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod); + /// + /// Asserts whether requests were made with a given HTTP Method. + /// + /// The that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpMethod(HttpMethod httpMethod, int expectedNumberOfRequests); + + /// + /// Asserts whether requests were made to a given URI based on a pattern. + /// + /// The uri pattern that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithRequestUri(string pattern); + + /// + /// Asserts whether requests were made to a given URI based on a pattern. + /// + /// The uri pattern that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithRequestUri(string pattern, int expectedNumberOfRequests); + + /// + /// Asserts whether requests were made using a specific HTTP Version. + /// + /// The implementation that hold all the request messages. + /// The that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion); + + /// + /// Asserts whether requests were made using a specific HTTP Version. + /// + /// The that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHttpVersion(Version httpVersion, int expectedNumberOfRequests); + + /// + /// Asserts whether requests were made with a specific header name. Values are ignored. + /// + /// The name of the header that is expected. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName); + + /// + /// Asserts whether requests were made with a specific header name. Values are ignored. + /// + /// The name of the header that is expected. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, int expectedNumberOfRequests); + + /// + /// Asserts whether requests were made with a specific header name and value. + /// + /// The name of the header that is expected. + /// The value of the expected header, supports wildcards. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue); + + /// + /// Asserts whether requests were made with a specific header name and value. + /// + /// The name of the header that is expected. + /// The value of the expected header, supports wildcards. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithHeader(string headerName, string headerValue, int expectedNumberOfRequests); + + /// + /// Asserts whether requests were made with specific content. + /// + /// The expected content, supports wildcards. + /// The for further assertions. + public IHttpRequestMessagesCheck WithContent(string pattern); + + /// + /// Asserts whether requests were made with specific content. + /// + /// The expected content, supports wildcards. + /// The expected number of requests. + /// The for further assertions. + public IHttpRequestMessagesCheck WithContent(string pattern, int expectedNumberOfRequests); } diff --git a/src/TestableHttpClient/PublicAPI.Shipped.txt b/src/TestableHttpClient/PublicAPI.Shipped.txt index 0897b89..8ddafdb 100644 --- a/src/TestableHttpClient/PublicAPI.Shipped.txt +++ b/src/TestableHttpClient/PublicAPI.Shipped.txt @@ -75,22 +75,12 @@ TestableHttpClient.HttpRequestMessageAssertionException.HttpRequestMessageAssert TestableHttpClient.HttpRequestMessageAssertionException.HttpRequestMessageAssertionException(string! message, System.Exception! innerException) -> void TestableHttpClient.HttpRequestMessagesCheckExtensions -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! pattern) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! pattern, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContentHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContentHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContentHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithContentHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithFormUrlEncodedContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Collections.Generic.IEnumerable>! nameValueCollection) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithFormUrlEncodedContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Collections.Generic.IEnumerable>! nameValueCollection, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHttpMethod(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Net.Http.HttpMethod! httpMethod) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHttpMethod(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Net.Http.HttpMethod! httpMethod, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHttpVersion(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Version! httpVersion) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithHttpVersion(this TestableHttpClient.IHttpRequestMessagesCheck! check, System.Version! httpVersion, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithJsonContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, object? jsonObject) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithJsonContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, object? jsonObject, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithJsonContent(this TestableHttpClient.IHttpRequestMessagesCheck! check, object? jsonObject, System.Text.Json.JsonSerializerOptions! jsonSerializerOptions) -> TestableHttpClient.IHttpRequestMessagesCheck! @@ -99,8 +89,6 @@ static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestHeader(t static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue) -> TestableHttpClient.IHttpRequestMessagesCheck! static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestHeader(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! headerName, string! headerValue, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestUri(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! pattern) -> TestableHttpClient.IHttpRequestMessagesCheck! -static TestableHttpClient.HttpRequestMessagesCheckExtensions.WithRequestUri(this TestableHttpClient.IHttpRequestMessagesCheck! check, string! pattern, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! TestableHttpClient.Utils.UriPatternParserException TestableHttpClient.Utils.UriPatternParserException.UriPatternParserException() -> void diff --git a/src/TestableHttpClient/PublicAPI.Unshipped.txt b/src/TestableHttpClient/PublicAPI.Unshipped.txt index b7208c0..f9efd0a 100644 --- a/src/TestableHttpClient/PublicAPI.Unshipped.txt +++ b/src/TestableHttpClient/PublicAPI.Unshipped.txt @@ -1 +1,13 @@ -override TestableHttpClient.TestableHttpMessageHandler.Dispose(bool disposing) -> void \ No newline at end of file +override TestableHttpClient.TestableHttpMessageHandler.Dispose(bool disposing) -> void +TestableHttpClient.IHttpRequestMessagesCheck.WithContent(string! pattern) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithContent(string! pattern, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHeader(string! headerName) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHeader(string! headerName, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHeader(string! headerName, string! headerValue) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHeader(string! headerName, string! headerValue, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHttpMethod(System.Net.Http.HttpMethod! httpMethod) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHttpMethod(System.Net.Http.HttpMethod! httpMethod, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHttpVersion(System.Version! httpVersion) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithHttpVersion(System.Version! httpVersion, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithRequestUri(string! pattern) -> TestableHttpClient.IHttpRequestMessagesCheck! +TestableHttpClient.IHttpRequestMessagesCheck.WithRequestUri(string! pattern, int expectedNumberOfRequests) -> TestableHttpClient.IHttpRequestMessagesCheck! \ No newline at end of file From ee96b3114131252e32ffe7b115a66749f2573429 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 15:43:56 +0100 Subject: [PATCH 09/12] Replace NSubstitute with a small amount of custom mocks --- .../WithRequestUri.cs | 3 +- .../TestableHttpClient.Tests.csproj | 8 --- .../CreateClientWithConfigurer.cs | 9 ++- ...entWithConfigurerAndHttpMessageHandlers.cs | 57 ++++++++----------- .../TestableHttpMessageHandlerTests.cs | 22 ++++--- 5 files changed, 44 insertions(+), 55 deletions(-) diff --git a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs index 7301a77..4ff0dd3 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessageAsserterTests/WithRequestUri.cs @@ -37,7 +37,8 @@ public void WithNonMatchingRequestUri_ThrowsHttpRequestMessageAssertionException using HttpRequestMessage request = new(HttpMethod.Get, "https://example.com"); HttpRequestMessageAsserter sut = new([request]); - Assert.Throws(() => sut.WithRequestUri("https://no-op.com/")); + var exception = Assert.Throws(() => sut.WithRequestUri("https://no-op.com/")); + } diff --git a/test/TestableHttpClient.Tests/TestableHttpClient.Tests.csproj b/test/TestableHttpClient.Tests/TestableHttpClient.Tests.csproj index 62c2a35..a07e5e0 100644 --- a/test/TestableHttpClient.Tests/TestableHttpClient.Tests.csproj +++ b/test/TestableHttpClient.Tests/TestableHttpClient.Tests.csproj @@ -9,14 +9,6 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - diff --git a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurer.cs b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurer.cs index 241c7c1..b959783 100644 --- a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurer.cs +++ b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurer.cs @@ -1,6 +1,4 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests; +namespace TestableHttpClient.Tests; public partial class TestableHttpMessageHandlerExtensionsTests { @@ -63,11 +61,12 @@ public void CreateClientWithConfigurer_WhenConfiguringBaseAddress_DoesNotOverrid [Fact] public void CreateClientWithConfigurer_CallsConfigureClientWithClientToReturn() { + HttpClient? capturedClient= null; using TestableHttpMessageHandler sut = new(); - Action configureClient = Substitute.For>(); + void configureClient(HttpClient client) => capturedClient = client; using var client = sut.CreateClient(configureClient); - configureClient.Received(1).Invoke(client); + Assert.Same(client, capturedClient); } } diff --git a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurerAndHttpMessageHandlers.cs b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurerAndHttpMessageHandlers.cs index bd02a07..87cbfdf 100644 --- a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurerAndHttpMessageHandlers.cs +++ b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerExtensionsTests/CreateClientWithConfigurerAndHttpMessageHandlers.cs @@ -1,6 +1,4 @@ -using NSubstitute; - -namespace TestableHttpClient.Tests; +namespace TestableHttpClient.Tests; public partial class TestableHttpMessageHandlerExtensionsTests { @@ -10,9 +8,8 @@ public void CreateClientWithConfigurerAndHttpMessageHandlers_NullTestableHttpMes TestableHttpMessageHandler sut = null!; static void configureClient(HttpClient _) { } - var handlers = Enumerable.Empty(); - - var exception = Assert.Throws(() => sut.CreateClient(configureClient, handlers)); + + var exception = Assert.Throws(() => sut.CreateClient(configureClient, [])); Assert.Equal("handler", exception.ParamName); } @@ -21,9 +18,8 @@ public void CreateClientWithConfigurerAndHttpMessageHandlers_NullConfigureAction { using TestableHttpMessageHandler sut = new(); Action configureClient = null!; - var handlers = Enumerable.Empty(); - - var exception = Assert.Throws(() => sut.CreateClient(configureClient, handlers)); + + var exception = Assert.Throws(() => sut.CreateClient(configureClient, [])); Assert.Equal("configureClient", exception.ParamName); } @@ -42,13 +38,13 @@ static void configureClient(HttpClient _) { } [Fact] public void CreateClientWithConfigurerAndHttpMessageHandlers_CallsConfigureClientWithClientToReturn() { + HttpClient? capturedClient = null; using TestableHttpMessageHandler sut = new(); - var configureClient = Substitute.For>(); - var handlers = Enumerable.Empty(); - - using var client = sut.CreateClient(configureClient, handlers); + void configureClient(HttpClient client) => capturedClient = client; + + using var client = sut.CreateClient(configureClient, []); - configureClient.Received(1).Invoke(client); + Assert.Same(client, capturedClient); } [Fact] @@ -56,19 +52,16 @@ public void CreateClientWithConfigurerAndHttpMessageHandlers_WhenHttpMessageHand { using TestableHttpMessageHandler sut = new(); static void configureClient(HttpClient _) { } - IEnumerable handlers = - [ - Substitute.For(), - Substitute.For() - ]; + using TestDelegateHandler delegate1 = new(); + using TestDelegateHandler delegate2 = new(); - using var client = sut.CreateClient(configureClient, handlers); + using var client = sut.CreateClient(configureClient, [delegate1, delegate2]); var handler = GetPrivateHandler(client); var delegatingHandler1 = Assert.IsAssignableFrom(handler); - Assert.Same(handlers.First(), delegatingHandler1); + Assert.Same(delegate1, delegatingHandler1); var delegatingHandler2 = Assert.IsAssignableFrom(delegatingHandler1.InnerHandler); - Assert.Same(handlers.Last(), delegatingHandler2); + Assert.Same(delegate2, delegatingHandler2); Assert.Same(sut, delegatingHandler2.InnerHandler); } @@ -77,13 +70,10 @@ public void CreateClientWithConfigurerAndHttpMessageHandlers_ByDefault_SetsDefau { using TestableHttpMessageHandler sut = new(); static void configureClient(HttpClient client) { } - IEnumerable handlers = - [ - Substitute.For(), - Substitute.For() - ]; + using TestDelegateHandler delegate1 = new(); + using TestDelegateHandler delegate2 = new(); - using var client = sut.CreateClient(configureClient, handlers); + using var client = sut.CreateClient(configureClient, [delegate1, delegate2]); Assert.Equal(new Uri("https://localhost"), client.BaseAddress); } @@ -93,15 +83,14 @@ public void CreateClientWithConfigurerAndHttpMessageHandlers_WhenConfiguringBase { using TestableHttpMessageHandler sut = new(); static void configureClient(HttpClient client) { client.BaseAddress = new Uri("https://example"); } - IEnumerable handlers = - [ - Substitute.For(), - Substitute.For() - ]; + using TestDelegateHandler delegate1 = new(); + using TestDelegateHandler delegate2 = new(); - using var client = sut.CreateClient(configureClient, handlers); + using var client = sut.CreateClient(configureClient, [delegate1, delegate2]); Assert.Equal(new Uri("https://example"), client.BaseAddress); } + + private sealed class TestDelegateHandler : DelegatingHandler { } } diff --git a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerTests.cs b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerTests.cs index 9539673..655b94e 100644 --- a/test/TestableHttpClient.Tests/TestableHttpMessageHandlerTests.cs +++ b/test/TestableHttpClient.Tests/TestableHttpMessageHandlerTests.cs @@ -1,8 +1,6 @@ using System.Collections.Concurrent; using System.Threading; -using NSubstitute; - namespace TestableHttpClient.Tests; public class TestableHttpMessageHandlerTests @@ -40,11 +38,8 @@ public async Task SendAsync_WhenMultipleRequestsAreMade_AllRequestsAreLogged() [Fact] public async Task SendAsync_ByDefault_CallsExecutAsyncOnIResponse() { - IResponse mockedResponse = Substitute.For(); + CustomResponse mockedResponse = new(); HttpResponseContext? context = null; - mockedResponse.ExecuteAsync(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask) - .AndDoes(x => context = x[0] as HttpResponseContext); using TestableHttpMessageHandler sut = new(); sut.RespondWith(mockedResponse); @@ -52,6 +47,7 @@ public async Task SendAsync_ByDefault_CallsExecutAsyncOnIResponse() using HttpRequestMessage request = new(HttpMethod.Get, new Uri("https://example.com/")); using HttpResponseMessage response = await client.SendAsync(request, TestContext.Current.CancellationToken); + context = mockedResponse.Context; Assert.NotNull(context); Assert.Same(request, context.HttpRequestMessage); Assert.Same(response, context.HttpResponseMessage); @@ -142,7 +138,7 @@ public async Task ClearRequests_ByDefault_ShouldClearRequests() public void GetAsync_ShouldNotHang() { using TestableHttpMessageHandler sut = new(); - sut.RespondWith(Responses.Delayed(new CustomResponse(), TimeSpan.FromSeconds(1))); + sut.RespondWith(Responses.Delayed(new CustomAsyncResponse(), TimeSpan.FromSeconds(1))); bool doesNotHang = Task.Run(() => { @@ -157,6 +153,18 @@ public void GetAsync_ShouldNotHang() } private sealed class CustomResponse : IResponse + { + public HttpResponseContext? Context { get; private set; } + + public Task ExecuteAsync(HttpResponseContext context, CancellationToken cancellationToken) + { + Context = context; + return Task.CompletedTask; + } + } + + // This class intentionally used the wrong patterns for running async code, please do not use in production! + private sealed class CustomAsyncResponse : IResponse { public Task ExecuteAsync(HttpResponseContext context, CancellationToken cancellationToken) { From 0920c0d9c6429394ab1e8117a5b0cf05f0f00e3b Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 19:46:13 +0100 Subject: [PATCH 10/12] Use WithHeader implementation for WithRequestHeader and WithContentHeader --- CHANGELOG.md | 1 + .../HttpRequestMessagesCheckExtensions.cs | 62 ++++++++++--------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 335c4a1..2a944ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - The TestableHttpMessageHandler now makes a clone of the original request, so that the original request can be disposed. This change also makes it possible to assert the content on .NET Framework. +- The methods `WithRequestHeader` and `WithContentHeader` now work the same as `WithHeader`, this might lead to slight changes in the behavior since now the headers from both the request and the content of that request are checked. - Moved `WithHttpMethod`, `WithRequestUri`, `WithHttpVersion`, `WithHeader` and `WithContent` from `HttpRequestMessagesCheckExtensions` to `HttpRequestMessageAsserter` and specify them on the `IHttpRequestMessagesCheck` interface. ## [0.11] - 2024-06-15 diff --git a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs index a0c6cd6..297b4e2 100644 --- a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs @@ -10,7 +10,12 @@ public static class HttpRequestMessagesCheckExtensions /// The name of the header that is expected. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName) => WithRequestHeader(check, headerName, (int?)null); + public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName) + { + Guard.ThrowIfNull(check); + + return check.WithHeader(headerName); + } /// /// Asserts whether requests were made with a specific header name. Values are ignored. @@ -21,15 +26,11 @@ public static class HttpRequestMessagesCheckExtensions /// The expected number of requests. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) => WithRequestHeader(check, headerName, (int?)expectedNumberOfRequests); - - [Obsolete("Use WithHeader instead.")] - private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - return check.WithFilter(x => x.HasRequestHeader(headerName), expectedNumberOfRequests, $"request header '{headerName}'"); + return check.WithHeader(headerName, expectedNumberOfRequests); } /// @@ -41,7 +42,12 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The value of the expected header, supports wildcards. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) => WithRequestHeader(check, headerName, headerValue, null); + public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) + { + Guard.ThrowIfNull(check); + + return check.WithHeader(headerName, headerValue); + } /// /// Asserts whether requests were made with a specific header name and value. @@ -53,16 +59,11 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The expected number of requests. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) => WithRequestHeader(check, headerName, headerValue, (int?)expectedNumberOfRequests); - - [Obsolete("Use WithHeader instead.")] - private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - Guard.ThrowIfNullOrEmpty(headerValue); - return check.WithFilter(x => x.HasRequestHeader(headerName, headerValue), expectedNumberOfRequests, $"request header '{headerName}' and value '{headerValue}'"); + return check.WithHeader(headerName, headerValue, expectedNumberOfRequests); } /// @@ -73,7 +74,12 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The name of the header that is expected. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName) => WithContentHeader(check, headerName, (int?)null); + public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName) + { + Guard.ThrowIfNull(check); + + return check.WithHeader(headerName); + } /// /// Asserts whether requests were made with a specific header name. Values are ignored. @@ -84,15 +90,11 @@ private static IHttpRequestMessagesCheck WithRequestHeader(this IHttpRequestMess /// The expected number of requests. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) => WithContentHeader(check, headerName, (int?)expectedNumberOfRequests); - - [Obsolete("Use WithHeader instead.")] - private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - return check.WithFilter(x => x.HasContentHeader(headerName), expectedNumberOfRequests, $"content header '{headerName}'"); + return check.WithHeader(headerName, expectedNumberOfRequests); } /// @@ -104,7 +106,12 @@ private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMess /// The value of the expected header, supports wildcards. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) => WithContentHeader(check, headerName, headerValue, null); + public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue) + { + Guard.ThrowIfNull(check); + + return check.WithHeader(headerName, headerValue); + } /// /// Asserts whether requests were made with a specific header name and value. @@ -116,16 +123,11 @@ private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMess /// The expected number of requests. /// The for further assertions. [Obsolete("Use WithHeader instead.")] - public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) => WithContentHeader(check, headerName, headerValue, (int?)expectedNumberOfRequests); - - [Obsolete("Use WithHeader instead.")] - private static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessagesCheck check, string headerName, string headerValue, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); - Guard.ThrowIfNullOrEmpty(headerName); - Guard.ThrowIfNullOrEmpty(headerValue); - return check.WithFilter(x => x.HasContentHeader(headerName, headerValue), expectedNumberOfRequests, $"content header '{headerName}' and value '{headerValue}'"); + return check.WithHeader(headerName, headerValue, expectedNumberOfRequests); } /// From 7f12de4a8abbb9583265815c6f59c14f6342e1bc Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 20:08:26 +0100 Subject: [PATCH 11/12] Change WithJsonContent and WithFormUrlEncodedContent to use WithContent and WithHeader methods instead of their own implementation. --- .../HttpRequestMessagesCheckExtensions.cs | 61 +++++++++++++++---- .../WithFormUrlEncodedContent.cs | 4 +- .../WithJsonContent.cs | 4 +- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs index 297b4e2..f10f44d 100644 --- a/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs +++ b/src/TestableHttpClient/HttpRequestMessagesCheckExtensions.cs @@ -1,4 +1,6 @@ -namespace TestableHttpClient; +using System.Text.Json; + +namespace TestableHttpClient; public static class HttpRequestMessagesCheckExtensions { @@ -136,7 +138,16 @@ public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessa /// The implementation that hold all the request messages. /// The object representation of the expected request content. /// The for further assertions. - public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject) => WithJsonContent(check, jsonObject, null, null); + public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject) + { + + Guard.ThrowIfNull(check); + + var jsonString = JsonSerializer.Serialize(jsonObject, check.Options.JsonSerializerOptions); + + return check.WithContent(jsonString) + .WithHeader("Content-Type", "application/json*"); + } /// /// Asserts whether requests are made with specific json content. @@ -145,7 +156,15 @@ public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessa /// The object representation of the expected request content. /// The serializer options that should be used for serializing te content. /// The for further assertions. - public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, JsonSerializerOptions jsonSerializerOptions) => WithJsonContent(check, jsonObject, jsonSerializerOptions, null); + public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, JsonSerializerOptions jsonSerializerOptions) + { + Guard.ThrowIfNull(check); + + var jsonString = JsonSerializer.Serialize(jsonObject, jsonSerializerOptions ?? check.Options.JsonSerializerOptions); + + return check.WithContent(jsonString) + .WithHeader("Content-Type", "application/json*"); + } /// /// Asserts whether requests are made with specific json content. @@ -154,7 +173,15 @@ public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessa /// The object representation of the expected request content. /// The expected number of requests. /// The for further assertions. - public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, int expectedNumberOfRequests) => WithJsonContent(check, jsonObject, null, (int?)expectedNumberOfRequests); + public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, int expectedNumberOfRequests) + { + Guard.ThrowIfNull(check); + + var jsonString = JsonSerializer.Serialize(jsonObject, check.Options.JsonSerializerOptions); + + return check.WithContent(jsonString, expectedNumberOfRequests) + .WithHeader("Content-Type", "application/json*", expectedNumberOfRequests); + } /// /// Asserts whether requests are made with specific json content. @@ -164,15 +191,14 @@ public static IHttpRequestMessagesCheck WithContentHeader(this IHttpRequestMessa /// The serializer options that should be used for serializing the content. /// The expected number of requests. /// The for further assertions. - public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, JsonSerializerOptions jsonSerializerOptions, int expectedNumberOfRequests) => WithJsonContent(check, jsonObject, jsonSerializerOptions, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, JsonSerializerOptions? jsonSerializerOptions, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessagesCheck check, object? jsonObject, JsonSerializerOptions jsonSerializerOptions, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); var jsonString = JsonSerializer.Serialize(jsonObject, jsonSerializerOptions ?? check.Options.JsonSerializerOptions); - return check.WithFilter(x => x.HasContent(jsonString) && x.HasHeader("Content-Type", "application/json*"), expectedNumberOfRequests, $"json content '{jsonString}'"); + return check.WithContent(jsonString, expectedNumberOfRequests) + .WithHeader("Content-Type", "application/json*", expectedNumberOfRequests); } /// @@ -181,7 +207,17 @@ private static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessag /// The implementation that hold all the request messages. /// The collection of key/value pairs that should be url encoded. /// The for further assertions. - public static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpRequestMessagesCheck check, IEnumerable> nameValueCollection) => WithFormUrlEncodedContent(check, nameValueCollection, null); + public static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpRequestMessagesCheck check, IEnumerable> nameValueCollection) + { + Guard.ThrowIfNull(check); + Guard.ThrowIfNull(nameValueCollection); + + using var content = new FormUrlEncodedContent(nameValueCollection); + var contentString = content.ReadAsStringAsync().Result; + + return check.WithContent(contentString) + .WithHeader("Content-Type", "application/x-www-form-urlencoded*"); + } /// /// Asserts whether requests are made with specific url encoded content. @@ -190,9 +226,7 @@ private static IHttpRequestMessagesCheck WithJsonContent(this IHttpRequestMessag /// The collection of key/value pairs that should be url encoded. /// The expected number of requests. /// The for further assertions. - public static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpRequestMessagesCheck check, IEnumerable> nameValueCollection, int expectedNumberOfRequests) => WithFormUrlEncodedContent(check, nameValueCollection, (int?)expectedNumberOfRequests); - - private static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpRequestMessagesCheck check, IEnumerable> nameValueCollection, int? expectedNumberOfRequests) + public static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpRequestMessagesCheck check, IEnumerable> nameValueCollection, int expectedNumberOfRequests) { Guard.ThrowIfNull(check); Guard.ThrowIfNull(nameValueCollection); @@ -200,6 +234,7 @@ private static IHttpRequestMessagesCheck WithFormUrlEncodedContent(this IHttpReq using var content = new FormUrlEncodedContent(nameValueCollection); var contentString = content.ReadAsStringAsync().Result; - return check.WithFilter(x => x.HasContent(contentString) && x.HasHeader("Content-Type", "application/x-www-form-urlencoded*"), expectedNumberOfRequests, $"form url encoded content '{contentString}'"); + return check.WithContent(contentString, expectedNumberOfRequests) + .WithHeader("Content-Type", "application/x-www-form-urlencoded*", expectedNumberOfRequests); } } diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs index d147730..3d98122 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithFormUrlEncodedContent.cs @@ -88,7 +88,7 @@ public void WithFormUrlEncodedContent_WithoutNumberOfRequests_RequestWithNotMatc var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent([new KeyValuePair("username", "alice")])); - Assert.Equal("Expected at least one request to be made with form url encoded content 'username=alice', but no requests were made.", exception.Message); + Assert.Equal("Expected at least one request to be made with content 'username=alice', but no requests were made.", exception.Message); } [Fact] @@ -103,6 +103,6 @@ public void WithFormUrlEncodedContent_WithoutNumberOfRequests_RequestWithNotMatc var exception = Assert.Throws(() => sut.WithFormUrlEncodedContent([new KeyValuePair("username", "alice")])); - Assert.Equal("Expected at least one request to be made with form url encoded content 'username=alice', but no requests were made.", exception.Message); + Assert.Equal("Expected at least one request to be made with content 'username=alice', header 'Content-Type' and value 'application/x-www-form-urlencoded*', but no requests were made.", exception.Message); } } diff --git a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs index 8d9fa57..29b31c1 100644 --- a/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs +++ b/test/TestableHttpClient.Tests/HttpRequestMessagesCheckExtensionsTests/WithJsonContent.cs @@ -47,7 +47,7 @@ public void WithJsonContent_RequestWithDifferentContent_ThrowsHttpRequestMessage HttpRequestMessageAsserter sut = new([request]); var exception = Assert.Throws(() => sut.WithJsonContent(null)); - Assert.Equal("Expected at least one request to be made with json content 'null', but no requests were made.", exception.Message); + Assert.Equal("Expected at least one request to be made with content 'null', but no requests were made.", exception.Message); } [Fact] @@ -60,6 +60,6 @@ public void WithJsonContent_RequestWithDifferentContentType_ThrowsHttpRequestMes HttpRequestMessageAsserter sut = new([request]); var exception = Assert.Throws(() => sut.WithJsonContent(null)); - Assert.Equal("Expected at least one request to be made with json content 'null', but no requests were made.", exception.Message); + Assert.Equal("Expected at least one request to be made with content 'null', header 'Content-Type' and value 'application/json*', but no requests were made.", exception.Message); } } From 20573052f12c5bc55a410307b0bb6598a0083653 Mon Sep 17 00:00:00 2001 From: David Perfors Date: Sun, 16 Nov 2025 20:19:34 +0100 Subject: [PATCH 12/12] Deprecate WithFilter on IHttpRequestMessagesCheck --- CHANGELOG.md | 1 + src/TestableHttpClient/IHttpRequestMessagesCheck.cs | 3 +++ .../AssertingRequests.cs | 12 ------------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a944ee..d13f584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [0.12] - unplanned ### Deprecated - The methods `WithRequestHeader` and `WithContentHeader` are deprecated, please use `WithHeader` instead. +- The `WithFilter` method is deprecated and will be made internal or be removed. If there is a specific assertion that is missing, please open an issue. ### Removed - .NET 6.0 target, since it is no longer supported diff --git a/src/TestableHttpClient/IHttpRequestMessagesCheck.cs b/src/TestableHttpClient/IHttpRequestMessagesCheck.cs index 9fcd12f..8f19772 100644 --- a/src/TestableHttpClient/IHttpRequestMessagesCheck.cs +++ b/src/TestableHttpClient/IHttpRequestMessagesCheck.cs @@ -16,6 +16,7 @@ public interface IHttpRequestMessagesCheck /// The filter to filter requests with before asserting. /// The name of the condition, used in the exception message. /// The for further assertions. + [Obsolete("WithFilter will be made internal, since it should no longer be necesary to use.")] public IHttpRequestMessagesCheck WithFilter(Func requestFilter, string condition); /// @@ -25,6 +26,7 @@ public interface IHttpRequestMessagesCheck /// The expected number of requests. /// The name of the condition, used in the exception message. /// The for further assertions. + [Obsolete("WithFilter will be made internal, since it should no longer be necesary to use.")] public IHttpRequestMessagesCheck WithFilter(Func requestFilter, int expectedNumberOfRequests, string condition); /// @@ -34,6 +36,7 @@ public interface IHttpRequestMessagesCheck /// The expected number of requests, when null is passed "at least one" is presumed. /// The name of the condition, used in the exception message. /// The for further assertions. + [Obsolete("WithFilter will be made internal, since it should no longer be necesary to use.")] public IHttpRequestMessagesCheck WithFilter(Func requestFilter, int? expectedNumberOfRequests, string condition); /// diff --git a/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs b/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs index 4e539a4..42e8b5c 100644 --- a/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs +++ b/test/TestableHttpClient.IntegrationTests/AssertingRequests.cs @@ -200,16 +200,4 @@ public async Task AssertJsonContent() testHandler.ShouldHaveMadeRequests().WithJsonContent(new { }); } - - [Fact] - public async Task CustomAssertions() - { - using TestableHttpMessageHandler testHandler = new(); - using HttpClient client = new(testHandler); - - using StringContent content = new("", Encoding.UTF8, "application/json"); - _ = await client.PostAsync("https://httpbin.org/post", content, TestContext.Current.CancellationToken); - - testHandler.ShouldHaveMadeRequests().WithFilter(x => x.Content is not null && x.Content.Headers.ContentType?.MediaType == "application/json", ""); - } }