Skip to content

Commit

Permalink
Make API more consistent
Browse files Browse the repository at this point in the history
* Renamed several members on HttpResponseMessageBuilder
* Mark HasHttpVersion with string parameter as obsolete.
* Do not support null for expectedContent
* Add missing HasContent methods on HttpRequestMessageExtensions and HttpResponseMessageExtensions.
  • Loading branch information
dnperfors authored May 24, 2020
1 parent 268dc73 commit 36d77bd
Show file tree
Hide file tree
Showing 20 changed files with 355 additions and 197 deletions.
10 changes: 4 additions & 6 deletions src/TestableHttpClient.NFluent/HttpResponseMessageChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,19 +222,17 @@ public static class HttpResponseMessageChecks
/// <param name="check">The fluent check to be extended.</param>
/// <param name="expectedContent">The content that the response should contain, this could be a pattern that includes an astrix ('*').</param>
/// <returns>A check link.</returns>
public static ICheckLink<ICheck<HttpResponseMessage?>> HasContent(this ICheck<HttpResponseMessage?> check, string? expectedContent)
public static ICheckLink<ICheck<HttpResponseMessage?>> HasContent(this ICheck<HttpResponseMessage?> check, string expectedContent)
{
var checkLogic = ExtensibilityHelper.BeginCheck(check)
.SetSutName("response")
.FailIfNull();

if (expectedContent == null)
{
#pragma warning disable CS8602 // Dereference of a possibly null reference. Justification = "Null reference check is performed by the FailIfNull check"
checkLogic.CheckSutAttributes(sut => sut.Content, "content")
#pragma warning restore CS8602 // Dereference of a possibly null reference.
.FailWhen(sut => sut != null, "The {0} should be null.", MessageOption.NoCheckedBlock | MessageOption.NoExpectedBlock)
.OnNegate("The {0} should not be null.", MessageOption.NoCheckedBlock | MessageOption.NoExpectedBlock)
checkLogic
.Fail("The expected content should not be null, but it is.", MessageOption.NoCheckedBlock | MessageOption.NoExpectedBlock)
.CantBeNegated($"{nameof(HasContent)} with {nameof(expectedContent)} set to null")
.EndCheck();
}
else if (expectedContent.Contains("*"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
5 changes: 5 additions & 0 deletions src/TestableHttpClient/HttpRequestMessageAsserter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ public HttpRequestMessageAsserter WithJsonContent(object? jsonObject)
return With(x => x.HasContent(jsonString) && x.HasContentHeader("Content-Type", "application/json*"), $"json content '{jsonString}'");
}

/// <summary>
/// Asserts wheter requests are made with specific url encoded content.
/// </summary>
/// <param name="nameValueCollection">The collection of key/value pairs that should be url encoded.</param>
/// <returns>The <seealso cref="HttpRequestMessageAsserter"/> for further assertions.</returns>
public HttpRequestMessageAsserter WithFormUrlEncodedContent(IEnumerable<KeyValuePair<string, string>> nameValueCollection)
{
if (nameValueCollection == null)
Expand Down
15 changes: 15 additions & 0 deletions src/TestableHttpClient/HttpRequestMessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,21 @@ public static bool HasMatchingUri(this HttpRequestMessage httpRequestMessage, st
};
}

/// <summary>
/// Determines whether the request has content.
/// </summary>
/// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/> to check for content.</param>
/// <returns>true when the request has content; otherwise, false.</returns>
public static bool HasContent(this HttpRequestMessage httpRequestMessage)
{
if (httpRequestMessage == null)
{
throw new ArgumentNullException(nameof(httpRequestMessage));
}

return httpRequestMessage.Content != null;
}

/// <summary>
/// Determines whether the request content matches a string pattern.
/// </summary>
Expand Down
47 changes: 46 additions & 1 deletion src/TestableHttpClient/HttpResponseMessageBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@ public sealed class HttpResponseMessageBuilder
/// </summary>
/// <param name="httpVersion">The <see cref="HttpVersion"/> of the response.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
[Obsolete("Renamed to WithHttpVersion.", true)]
public HttpResponseMessageBuilder WithVersion(Version httpVersion)
{
return WithHttpVersion(httpVersion);
}

/// <summary>
/// Specifies the version of the response.
/// </summary>
/// <param name="httpVersion">The <see cref="HttpVersion"/> of the response.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
public HttpResponseMessageBuilder WithHttpVersion(Version httpVersion)
{
httpResponseMessage.Version = httpVersion;
return this;
Expand All @@ -35,7 +46,18 @@ public HttpResponseMessageBuilder WithVersion(Version httpVersion)
/// </summary>
/// <param name="statusCode">The <see cref="HttpStatusCode"/> of the response.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
public HttpResponseMessageBuilder WithStatusCode(HttpStatusCode statusCode)
[Obsolete("Renamed to WithHttpStatusCode.", true)]
public HttpResponseMessageBuilder WithStatusCode(HttpStatusCode httpStatusCode)
{
return WithHttpStatusCode(httpStatusCode);
}

/// <summary>
/// Specifies the status code of the response.
/// </summary>
/// <param name="statusCode">The <see cref="HttpStatusCode"/> of the response.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
public HttpResponseMessageBuilder WithHttpStatusCode(HttpStatusCode statusCode)
{
httpResponseMessage.StatusCode = statusCode;
return this;
Expand All @@ -46,7 +68,18 @@ public HttpResponseMessageBuilder WithStatusCode(HttpStatusCode statusCode)
/// </summary>
/// <param name="responseHeaderBuilder">The builder for configuring the response headers.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
[Obsolete("Renamed to WithResponseHeaders.", true)]
public HttpResponseMessageBuilder WithHeaders(Action<HttpResponseHeaders> responseHeaderBuilder)
{
return WithResponseHeaders(responseHeaderBuilder);
}

/// <summary>
/// Configure request headers using a builder by directly accessing the <see cref="HttpResponseHeaders"/>.
/// </summary>
/// <param name="responseHeaderBuilder">The builder for configuring the response headers.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
public HttpResponseMessageBuilder WithResponseHeaders(Action<HttpResponseHeaders> responseHeaderBuilder)
{
if (responseHeaderBuilder == null)
{
Expand All @@ -63,7 +96,19 @@ public HttpResponseMessageBuilder WithHeaders(Action<HttpResponseHeaders> respon
/// <param name="header">The name of the header to add.</param>
/// <param name="value">The value of the header to add.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
[Obsolete("Renamed to WithResponseHeader.", true)]
public HttpResponseMessageBuilder WithHeader(string header, string value)
{
return WithResponseHeader(header, value);
}

/// <summary>
/// Adds a request header to the response.
/// </summary>
/// <param name="header">The name of the header to add.</param>
/// <param name="value">The value of the header to add.</param>
/// <returns>The <see cref="HttpResponseMessageBuilder"/> for further building of the response.</returns>
public HttpResponseMessageBuilder WithResponseHeader(string header, string value)
{
if (string.IsNullOrEmpty(header))
{
Expand Down
15 changes: 15 additions & 0 deletions src/TestableHttpClient/HttpResponseMessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public static bool HasHttpVersion(this HttpResponseMessage httpResponseMessage,
/// <param name="httpResponseMessage">A <see cref="HttpResponseMessage"/> to check the correct version on.</param>
/// <param name="httpVersion">The expected version.</param>
/// <returns>true when the HttpVersion matches; otherwise, false.</returns>
[Obsolete("Use overload with System.Version instead.", true)]
public static bool HasHttpVersion(this HttpResponseMessage httpResponseMessage, string httpVersion)
{
if (httpResponseMessage == null)
Expand Down Expand Up @@ -198,6 +199,20 @@ public static bool HasContentHeader(this HttpResponseMessage httpResponseMessage
return httpResponseMessage.Content.Headers.HasHeader(headerName, headerValue);
}

/// <summary>
/// Determines whether the response has content.
/// </summary>
/// <param name="httpResponseMessage">A <see cref="HttpResponseMessage"/> to check for content.</param>
/// <returns>true when the response has content; otherwise, false.</returns>
public static bool HasContent(this HttpResponseMessage httpResponseMessage)
{
if (httpResponseMessage == null)
{
throw new ArgumentNullException(nameof(httpResponseMessage));
}
return httpResponseMessage.Content != null;
}

/// <summary>
/// Determines whether the response content matches a string pattern.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/TestableHttpClient/TestableHttpClient.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="4.7.1" />
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>

<Import Project="..\SharedUtilities\SharedUtilities.projitems" Label="Shared" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task UsingTestHandlerWithCustomResponseUsingBuilder_ReturnsCustomRe
using var testHandler = new TestableHttpMessageHandler();
testHandler.RespondWith(response =>
{
response.WithStatusCode(HttpStatusCode.Created)
response.WithHttpStatusCode(HttpStatusCode.Created)
.WithStringContent("HttpClient testing is easy");
});

Expand All @@ -59,8 +59,8 @@ public async Task UsingTestHandlerWithCustomResponseUsingBuilder_ReturnsCustomRe
public async Task UsingTestHandlerWithMultipleCustomRepsonse_ReturnsLastCustomResponse()
{
using var testHandler = new TestableHttpMessageHandler();
testHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.Created).WithStringContent("HttpClient testing is easy"));
testHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.NotFound).WithJsonContent("Not Found"));
testHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.Created).WithStringContent("HttpClient testing is easy"));
testHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.NotFound).WithJsonContent("Not Found"));

using var httpClient = new HttpClient(testHandler);
var result = await httpClient.GetAsync("http://httpbin.org/status/201");
Expand All @@ -73,7 +73,7 @@ public async Task UsingTestHandlerWithMultipleCustomRepsonse_ReturnsLastCustomRe
public async Task UsingTestHandlerWithCustomResponse_AlwaysReturnsSameCustomResponse()
{
using var testHandler = new TestableHttpMessageHandler();
testHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.Created).WithStringContent("HttpClient testing is easy"));
testHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.Created).WithStringContent("HttpClient testing is easy"));

using var httpClient = new HttpClient(testHandler);
var urls = new[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.4" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="Moq" Version="4.14.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.2.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public async Task ConfigureIHttpClientFactoryToUseTestableHttpClient()
{
// Create TestableHttpMessageHandler as usual.
var testableHttpMessageHandler = new TestableHttpMessageHandler();
testableHttpMessageHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.OK));
testableHttpMessageHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.OK));

// Create a mock for IHttpClientFactory
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
Expand All @@ -34,10 +34,10 @@ public async Task ConfigureIHttpClientFactoryToUseTestableHttpClient()
public async Task ConfigureMultiplehttpClientFactories()
{
var testableGithubHandler = new TestableHttpMessageHandler();
testableGithubHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.OK).WithHeader("Server", "github"));
testableGithubHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.OK).WithResponseHeader("Server", "github"));

var testableHttpBinHandler = new TestableHttpMessageHandler();
testableHttpBinHandler.RespondWith(response => response.WithStatusCode(HttpStatusCode.NotFound).WithHeader("Server", "httpbin"));
testableHttpBinHandler.RespondWith(response => response.WithHttpStatusCode(HttpStatusCode.NotFound).WithResponseHeader("Server", "httpbin"));

// Create a mock for IHttpClientFactory
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net.Http;
using System;
using System.Net.Http;

using NFluent;
using NFluent.Helpers;
Expand Down Expand Up @@ -37,51 +38,28 @@ public void HasContentWithPattern_WhenContentIsNull_DoesFail()
);
}

#nullable disable
[Fact]
public void HasContentWithPattern_WhenContentIsNullAndExpectedContentIsNull_DoesNotFail()
public void HasContentWithPattern_WhenExpectedContentIsNull_DoesFail()
{
using var sut = new HttpResponseMessage();

Check.That(sut).HasContent(null);
}

[Fact]
public void HasContentWithPattern_WhenContentIsNullAndExpectedContentIsNullAndNotIsUsed_DoesFail()
{
using var sut = new HttpResponseMessage();

Check.ThatCode(() => Check.That(sut).Not.HasContent(null))
.IsAFailingCheckWithMessage(
"",
"The checked response's content should not be null."
);
}

[Fact]
public void HasContentWithPattern_WhenContentIsNotNullAndExpectedContentIsNull_DoesFail()
{
using var sut = new HttpResponseMessage
{
Content = new StringContent("")
};

Check.ThatCode(() => Check.That(sut).HasContent(null))
.IsAFailingCheckWithMessage(
"",
"The checked response's content should be null."
"",
"The expected content should not be null, but it is."
);
}

[Fact]
public void HasContentWithPattern_WhenContentIsNotNullAndExpectedContentIsNullAndNotIsUsed_DoesNotFail()
public void HasContentWithPattern_WhenExpectedContentIsNullAndNotIsUsed_DoesFail()
{
using var sut = new HttpResponseMessage
{
Content = new StringContent("")
};
using var sut = new HttpResponseMessage();

Check.That(sut).Not.HasContent(null);
Check.ThatCode(() => Check.That(sut).Not.HasContent(null))
.Throws<InvalidOperationException>();
}
#nullable restore

[Fact]
public void HasContentWithPattern_WhenContentIsNullAndNotIsUsed_DoesNotFail()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NFluent" Version="2.7.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.2.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit 36d77bd

Please sign in to comment.