Skip to content

Commit 4cb3bd2

Browse files
authored
feat: add support for "Patch" (#360)
This PR adds comprehensive support for the HTTP PATCH method to Mockolate's HttpClient mocking capabilities, extending the existing HTTP verb coverage (GET, POST, PUT, DELETE) to include PATCH operations for .NET 8.0 and later. ### Key Changes: - Adds PatchAsync extension methods for both setup and verification scenarios - Implements support for both string and Uri-based request URIs with optional CancellationToken parameters - Includes comprehensive test coverage matching the patterns used for other HTTP verbs
1 parent 293efcd commit 4cb3bd2

6 files changed

Lines changed: 567 additions & 0 deletions

File tree

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#if NET8_0_OR_GREATER
2+
using System;
3+
using System.Net.Http;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Mockolate.Parameters;
7+
using Mockolate.Setup;
8+
9+
namespace Mockolate.Web;
10+
11+
#pragma warning disable S2325 // Methods and properties that don't access instance data should be static
12+
/// <summary>
13+
/// Extensions for mocking <see cref="HttpClient" />.
14+
/// </summary>
15+
public static partial class HttpClientExtensions
16+
{
17+
/// <inheritdoc cref="HttpClientExtensions" />
18+
extension(IMockMethodSetup<HttpClient> setup)
19+
{
20+
/// <summary>
21+
/// Setup for the method
22+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?)" />
23+
/// with the given <paramref name="requestUri" />.
24+
/// </summary>
25+
public IReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> PatchAsync(
26+
IParameter<string?>? requestUri,
27+
IParameter<HttpContent?>? content)
28+
=> setup.PatchAsync(requestUri, content, It.IsAny<CancellationToken>());
29+
30+
/// <summary>
31+
/// Setup for the method
32+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(Uri?, HttpContent?)" />
33+
/// with the given <paramref name="requestUri" />.
34+
/// </summary>
35+
public IReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> PatchAsync(
36+
IParameter<Uri?>? requestUri,
37+
IParameter<HttpContent?>? content)
38+
=> setup.PatchAsync(requestUri, content, It.IsAny<CancellationToken>());
39+
40+
/// <summary>
41+
/// Setup for the method
42+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?, System.Threading.CancellationToken)" />
43+
/// with the given <paramref name="requestUri" /> and <paramref name="cancellationToken" />.
44+
/// </summary>
45+
public IReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> PatchAsync(
46+
IParameter<string?>? requestUri,
47+
IParameter<HttpContent?>? content,
48+
IParameter<CancellationToken> cancellationToken)
49+
{
50+
if (setup is Mock<HttpClient> httpClientMock &&
51+
httpClientMock.ConstructorParameters[0] is IMockSubject<HttpMessageHandler> httpMessageHandlerMock &&
52+
httpMessageHandlerMock.Mock is IMockMethodSetup<HttpMessageHandler> httpMessageHandlerSetup)
53+
{
54+
ReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> methodSetup =
55+
new("System.Net.Http.HttpMessageHandler.SendAsync",
56+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
57+
new HttpStringUriParameter(requestUri),
58+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
59+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
60+
CastToMockRegistrationOrThrow(httpMessageHandlerSetup).SetupMethod(methodSetup);
61+
return methodSetup;
62+
}
63+
else
64+
{
65+
ReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> methodSetup =
66+
new("System.Net.Http.HttpMessageInvoker.SendAsync",
67+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
68+
new HttpStringUriParameter(requestUri),
69+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
70+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
71+
CastToMockRegistrationOrThrow(setup).SetupMethod(methodSetup);
72+
return methodSetup;
73+
}
74+
}
75+
76+
/// <summary>
77+
/// Setup for the method
78+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(Uri?, HttpContent?, System.Threading.CancellationToken)" />
79+
/// with the given <paramref name="requestUri" /> and <paramref name="cancellationToken" />.
80+
/// </summary>
81+
public IReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> PatchAsync(
82+
IParameter<Uri?>? requestUri,
83+
IParameter<HttpContent?>? content,
84+
IParameter<CancellationToken> cancellationToken)
85+
{
86+
if (setup is Mock<HttpClient> httpClientMock &&
87+
httpClientMock.ConstructorParameters[0] is IMockSubject<HttpMessageHandler> httpMessageHandlerMock &&
88+
httpMessageHandlerMock.Mock is IMockMethodSetup<HttpMessageHandler> httpMessageHandlerSetup)
89+
{
90+
ReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> methodSetup =
91+
new("System.Net.Http.HttpMessageHandler.SendAsync",
92+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
93+
new HttpRequestMessageParameter<Uri?>(r => r.RequestUri, requestUri),
94+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
95+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
96+
CastToMockRegistrationOrThrow(httpMessageHandlerSetup).SetupMethod(methodSetup);
97+
return methodSetup;
98+
}
99+
else
100+
{
101+
ReturnMethodSetup<Task<HttpResponseMessage>, HttpRequestMessage, CancellationToken> methodSetup =
102+
new("System.Net.Http.HttpMessageInvoker.SendAsync",
103+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
104+
new HttpRequestMessageParameter<Uri?>(r => r.RequestUri, requestUri),
105+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
106+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
107+
CastToMockRegistrationOrThrow(setup).SetupMethod(methodSetup);
108+
return methodSetup;
109+
}
110+
}
111+
}
112+
}
113+
#pragma warning restore S2325 // Methods and properties that don't access instance data should be static
114+
#endif
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#if NET8_0_OR_GREATER
2+
using System;
3+
using System.Net.Http;
4+
using System.Threading;
5+
using Mockolate.Parameters;
6+
using Mockolate.Verify;
7+
8+
namespace Mockolate.Web;
9+
10+
#pragma warning disable S2325 // Methods and properties that don't access instance data should be static
11+
/// <summary>
12+
/// Extensions for mocking <see cref="HttpClient" />.
13+
/// </summary>
14+
public static partial class HttpClientExtensions
15+
{
16+
/// <inheritdoc cref="HttpClientExtensions" />
17+
extension(IMockVerifyInvokedWithToStringWithEqualsWithGetHashCode<HttpClient> verifyInvoked)
18+
{
19+
/// <summary>
20+
/// Validates the invocations for the method
21+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?)" />
22+
/// with the given <paramref name="requestUri" />.
23+
/// </summary>
24+
public VerificationResult<HttpClient> PatchAsync(
25+
IParameter<string?>? requestUri,
26+
IParameter<HttpContent?>? content)
27+
=> verifyInvoked.PatchAsync(requestUri, content, It.IsAny<CancellationToken>());
28+
29+
/// <summary>
30+
/// Validates the invocations for the method
31+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?)" />
32+
/// with the given <paramref name="requestUri" />.
33+
/// </summary>
34+
public VerificationResult<HttpClient> PatchAsync(
35+
IParameter<Uri?>? requestUri,
36+
IParameter<HttpContent?>? content)
37+
=> verifyInvoked.PatchAsync(requestUri, content, It.IsAny<CancellationToken>());
38+
39+
/// <summary>
40+
/// Validates the invocations for the method
41+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?)" />
42+
/// with the given <paramref name="requestUri" /> and <paramref name="cancellationToken" />.
43+
/// </summary>
44+
public VerificationResult<HttpClient> PatchAsync(
45+
IParameter<string?>? requestUri,
46+
IParameter<HttpContent?>? content,
47+
IParameter<CancellationToken> cancellationToken)
48+
{
49+
if (verifyInvoked is Mock<HttpClient> httpClientMock &&
50+
httpClientMock.ConstructorParameters[0] is IMockSubject<HttpMessageHandler> httpMessageHandlerMock)
51+
{
52+
return httpMessageHandlerMock.Mock.Method("System.Net.Http.HttpMessageHandler.SendAsync",
53+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
54+
new HttpStringUriParameter(requestUri),
55+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
56+
new NamedParameter("cancellationToken", (IParameter)cancellationToken))
57+
.Map(httpClientMock.Subject);
58+
}
59+
60+
return CastToMockOrThrow(verifyInvoked).Method("System.Net.Http.HttpMessageInvoker.SendAsync",
61+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
62+
new HttpStringUriParameter(requestUri),
63+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
64+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
65+
}
66+
67+
/// <summary>
68+
/// Validates the invocations for the method
69+
/// <see cref="System.Net.Http.HttpClient.PatchAsync(string?, HttpContent?)" />
70+
/// with the given <paramref name="requestUri" /> and <paramref name="cancellationToken" />.
71+
/// </summary>
72+
public VerificationResult<HttpClient> PatchAsync(
73+
IParameter<Uri?>? requestUri,
74+
IParameter<HttpContent?>? content,
75+
IParameter<CancellationToken> cancellationToken)
76+
{
77+
if (verifyInvoked is Mock<HttpClient> httpClientMock &&
78+
httpClientMock.ConstructorParameters[0] is IMockSubject<HttpMessageHandler> httpMessageHandlerMock)
79+
{
80+
return httpMessageHandlerMock.Mock.Method("System.Net.Http.HttpMessageHandler.SendAsync",
81+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
82+
new HttpRequestMessageParameter<Uri?>(r => r.RequestUri, requestUri),
83+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
84+
new NamedParameter("cancellationToken", (IParameter)cancellationToken))
85+
.Map(httpClientMock.Subject);
86+
}
87+
88+
return CastToMockOrThrow(verifyInvoked).Method("System.Net.Http.HttpMessageInvoker.SendAsync",
89+
new NamedParameter("request", new HttpRequestMessageParameters(HttpMethod.Patch,
90+
new HttpRequestMessageParameter<Uri?>(r => r.RequestUri, requestUri),
91+
new HttpRequestMessageParameter<HttpContent?>(r => r.Content, content))),
92+
new NamedParameter("cancellationToken", (IParameter)cancellationToken));
93+
}
94+
}
95+
}
96+
#pragma warning restore S2325 // Methods and properties that don't access instance data should be static
97+
#endif

Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,10 @@ namespace Mockolate.Web
17601760
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> GetAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri) { }
17611761
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> GetAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
17621762
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> GetAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
1763+
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PatchAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
1764+
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PatchAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
1765+
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PatchAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
1766+
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PatchAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
17631767
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PostAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
17641768
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PostAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
17651769
public Mockolate.Setup.IReturnMethodSetup<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>, System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken> PostAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
@@ -1779,6 +1783,10 @@ namespace Mockolate.Web
17791783
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> GetAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri) { }
17801784
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> GetAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
17811785
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> GetAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
1786+
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PatchAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
1787+
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PatchAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
1788+
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PatchAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
1789+
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PatchAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }
17821790
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PostAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
17831791
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PostAsync(Mockolate.Parameters.IParameter<System.Uri?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content) { }
17841792
public Mockolate.Verify.VerificationResult<System.Net.Http.HttpClient> PostAsync(Mockolate.Parameters.IParameter<string?>? requestUri, Mockolate.Parameters.IParameter<System.Net.Http.HttpContent?>? content, Mockolate.Parameters.IParameter<System.Threading.CancellationToken> cancellationToken) { }

0 commit comments

Comments
 (0)