Skip to content

Commit 6ca7cfb

Browse files
authored
## v5.8.0 (#98)
- *Enhancement:* Extended the `MockHttpClientResponse.With*` methods to support optional _media type_ parameter to enable specification of the `Content-Type` header value. - *Enhancement:* Added `HttpResponseMessageAssertor.AssertContentTypeProblemJson` to enable asserting that the content type is `application/problem+json`. - *Fixed:* `HttpResponseMessageAssertor<TValue>.Value` no longer asserts the content type as `application/json` by default as this could not be overridden; this should be asserted explicitly using `AssertContentType()`, `AssertContentTypeJson()`, `AssertContentTypeProblemJson`, etc.
1 parent 5f5127d commit 6ca7cfb

File tree

9 files changed

+57
-25
lines changed

9 files changed

+57
-25
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
Represents the **NuGet** versions.
44

5+
## v5.8.0
6+
- *Enhancement:* Extended the `MockHttpClientResponse.With*` methods to support optional _media type_ parameter to enable specification of the `Content-Type` header value.
7+
- *Enhancement:* Added `HttpResponseMessageAssertor.AssertContentTypeProblemJson` to enable asserting that the content type is `application/problem+json`.
8+
- *Fixed:* `HttpResponseMessageAssertor<TValue>.Value` no longer asserts the content type as `application/json` by default as this could not be overridden; this should be asserted explicitly using `AssertContentType()`, `AssertContentTypeJson()`, `AssertContentTypeProblemJson`, etc.
9+
510
## v5.7.0
611
- *Enhancement:* Added `.NET10.0` support to all `UnitTestEx` packages.
712
- *Enhancement:* Added `AssertNoNamedHeader` to the `HttpResponseMessageAssertor` to enable asserting that a named header is not present in the response.

Common.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>5.7.0</Version>
3+
<Version>5.8.0</Version>
44
<LangVersion>preview</LangVersion>
55
<Authors>Avanade</Authors>
66
<Company>Avanade</Company>

src/UnitTestEx.Azure.Functions/Azure/Functions/FunctionTesterBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ protected override void ResetHost()
196196
{
197197
_host.Dispose();
198198
_host = null;
199-
Implementor.WriteLine("The underlying UnitTestEx 'FunctionTester' Host has been reset.");
199+
Implementor.WriteLine("");
200+
Implementor.WriteLine("** The underlying UnitTestEx 'FunctionTester' Host has been reset. **");
201+
Implementor.WriteLine("");
202+
200203
}
201204
}
202205
}

src/UnitTestEx/AspNetCore/ApiTesterBase.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ protected override void ResetHost()
100100
{
101101
_waf.Dispose();
102102
_waf = null;
103-
Implementor.WriteLine("The underlying UnitTestEx 'ApiTester' Host has been reset.");
103+
Implementor.WriteLine("");
104+
Implementor.WriteLine("** The underlying UnitTestEx 'ApiTester' Host has been reset. **");
105+
Implementor.WriteLine("");
104106
}
105107
}
106108
}
@@ -180,11 +182,8 @@ protected virtual void Dispose(bool disposing)
180182
if (_disposed)
181183
return;
182184

183-
if (_waf != null)
184-
{
185-
_waf.Dispose();
186-
_waf = null;
187-
}
185+
_waf?.Dispose();
186+
_waf = null;
188187

189188
_disposed = true;
190189
}

src/UnitTestEx/Assertors/HttpResponseMessageAssertorBaseT.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public TSelf Assert(HttpStatusCode statusCode, string? content)
6363
/// <returns>The <see cref="HttpResponseMessageAssertorBase{TSelf}"/> instance to support fluent-style method-chaining.</returns>
6464
public TSelf AssertContentTypeJson() => AssertContentType(MediaTypeNames.Application.Json);
6565

66+
/// <summary>
67+
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> content type is '<c>application/problem+json</c>'.
68+
/// </summary>
69+
/// <returns>The <see cref="HttpResponseMessageAssertorBase{TSelf}"/> instance to support fluent-style method-chaining.</returns>
70+
public TSelf AssertContentTypeProblemJson() => AssertContentType("application/problem+json");
71+
6672
/// <summary>
6773
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> content type is <see cref="MediaTypeNames.Text.Plain"/>.
6874
/// </summary>
@@ -187,6 +193,18 @@ public TSelf AssertETagHeader(System.Net.Http.Headers.EntityTagHeaderValue expec
187193
/// <returns>The <see cref="HttpResponseMessageAssertorBase{TSelf}"/> instance to support fluent-style method-chaining.</returns>
188194
public TSelf AssertForbidden() => Assert(HttpStatusCode.Forbidden);
189195

196+
/// <summary>
197+
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> is a <see cref="HttpStatusCode.InternalServerError"/>.
198+
/// </summary>
199+
/// <returns>The <see cref="HttpResponseMessageAssertorBase{TSelf}"/> instance to support fluent-style method-chaining.</returns>
200+
public TSelf AssertInternalServerError() => Assert(HttpStatusCode.InternalServerError);
201+
202+
/// <summary>
203+
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> is a <see cref="HttpStatusCode.ServiceUnavailable"/>.
204+
/// </summary>
205+
/// <returns>The <see cref="HttpResponseMessageAssertorBase{TSelf}"/> instance to support fluent-style method-chaining.</returns>
206+
public TSelf AssertServiceUnavailable() => Assert(HttpStatusCode.ServiceUnavailable);
207+
190208
/// <summary>
191209
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> contains the expected error <paramref name="messages"/>.
192210
/// </summary>

src/UnitTestEx/Assertors/HttpResponseMessageAssertorT.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@ internal HttpResponseMessageAssertor(HttpResponseMessageAssertor assertor) : thi
3939
/// Gets the response content as the deserialized JSON value.
4040
/// </summary>
4141
/// <returns>The result value.</returns>
42+
/// <remarks>The corresponding content type is not asserted, where this is required then <see cref="HttpResponseMessageAssertorBase.GetValue{T}(string?)"/> should be used.</remarks>
4243
public TValue? Value
4344
{
4445
get
4546
{
4647
if (!_valueIsDeserialized)
4748
{
48-
_value = GetValue<TValue>();
49+
_value = GetValue<TValue>(null);
4950
_valueIsDeserialized = true;
5051
}
5152

@@ -60,7 +61,7 @@ public TValue? Value
6061
/// <returns>The <see cref="HttpResponseMessageAssertor{TValue}"/> to support fluent-style method-chaining.</returns>
6162
public HttpResponseMessageAssertor<TValue> AssertLocationHeader(Func<TValue?, string> expected)
6263
{
63-
Implementor.AssertAreEqual(expected?.Invoke(GetValue<TValue>()), Response.Headers?.Location?.ToString(), $"Expected and Actual HTTP Response Header '{HeaderNames.Location}' values are not equal.");
64+
Implementor.AssertAreEqual(expected?.Invoke(Value), Response.Headers?.Location?.ToString(), $"Expected and Actual HTTP Response Header '{HeaderNames.Location}' values are not equal.");
6465
return this;
6566
}
6667

@@ -71,7 +72,7 @@ public HttpResponseMessageAssertor<TValue> AssertLocationHeader(Func<TValue?, st
7172
/// <returns>The <see cref="HttpResponseMessageAssertor{TValue}"/> to support fluent-style method-chaining.</returns>
7273
public HttpResponseMessageAssertor<TValue> AssertLocationHeader(Func<TValue?, Uri> expectedUri)
7374
{
74-
Implementor.AssertAreEqual(expectedUri?.Invoke(GetValue<TValue>()), Response.Headers?.Location, $"Expected and Actual HTTP Response Header '{HeaderNames.Location}' values are not equal.");
75+
Implementor.AssertAreEqual(expectedUri?.Invoke(Value), Response.Headers?.Location, $"Expected and Actual HTTP Response Header '{HeaderNames.Location}' values are not equal.");
7576
return this;
7677
}
7778

@@ -100,7 +101,7 @@ public HttpResponseMessageAssertor<TValue> AssertLocationHeaderContains(string e
100101
/// <param name="expected">The expected string.</param>
101102
/// <returns>The <see cref="HttpResponseMessageAssertor{TValue}"/> to support fluent-style method-chaining.</returns>
102103
public HttpResponseMessageAssertor<TValue> AssertLocationHeaderContains(Func<TValue?, string> expected)
103-
=> AssertLocationHeaderContains(expected?.Invoke(GetValue<TValue>())!);
104+
=> AssertLocationHeaderContains(expected?.Invoke(Value)!);
104105

105106
/// <summary>
106107
/// Asserts that the <see cref="HttpResponseMessageAssertorBase.Response"/> matches the <paramref name="expectedValue"/>.

src/UnitTestEx/Generic/GenericTesterCore.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ protected override void ResetHost()
155155
{
156156
_host.Dispose();
157157
_host = null;
158-
Implementor.WriteLine("The underlying UnitTestEx 'GenericTester' Host has been reset.");
158+
Implementor.WriteLine("");
159+
Implementor.WriteLine("** The underlying UnitTestEx 'GenericTester' Host has been reset. **");
160+
Implementor.WriteLine("");
159161
}
160162
}
161163
}

src/UnitTestEx/Mocking/MockHttpClient.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public MockHttpClient WithoutHttpMessageHandlers()
187187
}
188188

189189
/// <summary>
190-
/// Specifies that the configurations for the <see cref="GetHttpClient()"/> aer to be used.
190+
/// Specifies that the configurations for the <see cref="GetHttpClient()"/> are to be used.
191191
/// </summary>
192192
/// <returns>The <see cref="MockHttpClient"/> to support fluent-style method-chaining.</returns>
193193
/// <remarks>By default the <see cref="GetHttpClient()"/> configurations are not invoked.</remarks>
@@ -428,7 +428,6 @@ public void Add(MockHttpClient client)
428428
(res ?? new()).Add(seq.Respond());
429429
}
430430
});
431-
432431
}
433432
}
434433

src/UnitTestEx/Mocking/MockHttpClientResponse.cs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,32 +182,35 @@ public void With(HttpContent? content = null, HttpStatusCode? statusCode = null,
182182
/// </summary>
183183
/// <param name="content">The <see cref="string"/> content.</param>
184184
/// <param name="statusCode">The optional <see cref="HttpStatusCode"/> (defaults to <see cref="HttpStatusCode.OK"/>).</param>
185+
/// <param name="mediaType">The optional media type (defaults to <see cref="MediaTypeNames.Text.Plain"/>).</param>
185186
/// <param name="response">The optional action to enable additional configuration of the <see cref="HttpResponseMessage"/>.</param>
186-
public void With(string content, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null) => With(new StringContent(content ?? throw new ArgumentNullException(nameof(content))), statusCode, response);
187+
public void With(string content, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Text.Plain, Action<HttpResponseMessage>? response = null) => With(new StringContent(content ?? throw new ArgumentNullException(nameof(content)), null, mediaType!), statusCode, response);
187188

188189
/// <summary>
189190
/// Provides the mocked response using the <paramref name="value"/> which will be automatically converted to JSON content.
190191
/// </summary>
191192
/// <typeparam name="T">The value <see cref="Type"/>.</typeparam>
192-
/// <param name="value">The value to convert to <see cref="MediaTypeNames.Application.Json"/> content.</param>
193+
/// <param name="value">The value to convert to JSON content.</param>
193194
/// <param name="statusCode">The optional <see cref="HttpStatusCode"/> (defaults to <see cref="HttpStatusCode.OK"/>).</param>
195+
/// <param name="mediaType">The optional media type (defaults to <see cref="MediaTypeNames.Application.Json"/>).</param>
194196
/// <param name="response">The optional action to enable additional configuration of the <see cref="HttpResponseMessage"/>.</param>
195-
public void WithJson<T>(T value, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null) => WithJson(_clientRequest.JsonSerializer.Serialize(value, JsonWriteFormat.None), statusCode, response);
197+
public void WithJson<T>(T value, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Application.Json, Action<HttpResponseMessage>? response = null) => WithJson(_clientRequest.JsonSerializer.Serialize(value, JsonWriteFormat.None), statusCode, mediaType, response);
196198

197199
/// <summary>
198200
/// Provides the mocked response using the <paramref name="json"/> formatted string as the content.
199201
/// </summary>
200202
/// <param name="json">The <see cref="MediaTypeNames.Application.Json"/> content.</param>
201203
/// <param name="statusCode">The optional <see cref="HttpStatusCode"/> (defaults to <see cref="HttpStatusCode.OK"/>).</param>
204+
/// <param name="mediaType">The optional media type (defaults to <see cref="MediaTypeNames.Application.Json"/>).</param>
202205
/// <param name="response">The optional action to enable additional configuration of the <see cref="HttpResponseMessage"/>.</param>
203206
#if NET7_0_OR_GREATER
204-
public void WithJson([StringSyntax(StringSyntaxAttribute.Json)] string json, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null)
207+
public void WithJson([StringSyntax(StringSyntaxAttribute.Json)] string json, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Application.Json, Action<HttpResponseMessage>? response = null)
205208
#else
206-
public void WithJson(string json, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null)
209+
public void WithJson(string json, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Application.Json, Action<HttpResponseMessage>? response = null)
207210
#endif
208211
{
209212
var content = new StringContent(json ?? throw new ArgumentNullException(nameof(json)));
210-
content.Headers.ContentType = MediaTypeHeaderValue.Parse(MediaTypeNames.Application.Json);
213+
content.Headers.ContentType = MediaTypeHeaderValue.Parse(mediaType ?? MediaTypeNames.Application.Json);
211214
With(content, statusCode, response);
212215
}
213216

@@ -217,21 +220,23 @@ public void WithJson(string json, HttpStatusCode? statusCode = null, Action<Http
217220
/// <typeparam name="TAssembly">The <see cref="Type"/> used to infer <see cref="Assembly"/> that contains the embedded resource.</typeparam>
218221
/// <param name="resourceName">The embedded resource name (matches to the end of the fully qualifed resource name).</param>
219222
/// <param name="statusCode">The optional <see cref="HttpStatusCode"/> (defaults to <see cref="HttpStatusCode.OK"/>).</param>
223+
/// <param name="mediaType">The optional media type (defaults to <see cref="MediaTypeNames.Application.Json"/>).</param>
220224
/// <param name="response">The optional action to enable additional configuration of the <see cref="HttpResponseMessage"/>.</param>
221-
public void WithJsonResource<TAssembly>(string resourceName, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null)
222-
=> WithJsonResource(resourceName, statusCode, response, typeof(TAssembly).Assembly);
225+
public void WithJsonResource<TAssembly>(string resourceName, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Application.Json, Action<HttpResponseMessage>? response = null)
226+
=> WithJsonResource(resourceName, statusCode, mediaType, response, typeof(TAssembly).Assembly);
223227

224228
/// <summary>
225229
/// Provides the mocked response using the JSON formatted embedded resource string as the content.
226230
/// </summary>
227231
/// <param name="resourceName">The embedded resource name (matches to the end of the fully qualifed resource name).</param>
228232
/// <param name="statusCode">The optional <see cref="HttpStatusCode"/> (defaults to <see cref="HttpStatusCode.OK"/>).</param>
233+
/// <param name="mediaType">The optional media type (defaults to <see cref="MediaTypeNames.Application.Json"/>).</param>
229234
/// <param name="response">The optional action to enable additional configuration of the <see cref="HttpResponseMessage"/>.</param>
230235
/// <param name="assembly">The <see cref="Assembly"/> that contains the embedded resource; defaults to <see cref="Assembly.GetCallingAssembly"/>.</param>
231-
public void WithJsonResource(string resourceName, HttpStatusCode? statusCode = null, Action<HttpResponseMessage>? response = null, Assembly? assembly = null)
236+
public void WithJsonResource(string resourceName, HttpStatusCode? statusCode = null, string? mediaType = MediaTypeNames.Application.Json, Action<HttpResponseMessage>? response = null, Assembly? assembly = null)
232237
{
233238
using var sr = Resource.GetStream(resourceName, assembly ?? Assembly.GetCallingAssembly());
234-
WithJson(sr.ReadToEnd(), statusCode, response);
239+
WithJson(sr.ReadToEnd(), statusCode, mediaType, response);
235240
}
236241

237242
/// <summary>

0 commit comments

Comments
 (0)