generated from Avanade/avanade-template
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathUnitTestExExtensions.cs
440 lines (387 loc) · 27.3 KB
/
UnitTestExExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx
using Azure.Core.Amqp;
using Azure.Messaging.ServiceBus;
using CoreEx;
using CoreEx.AspNetCore.Http;
using CoreEx.AspNetCore.WebApis;
using CoreEx.Azure.ServiceBus;
using CoreEx.Events;
using CoreEx.Http;
using CoreEx.Mapping.Converters;
using CoreEx.Validation;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Net.Http;
using System.Net.Mime;
using System.Threading.Tasks;
using UnitTestEx.Abstractions;
using UnitTestEx.AspNetCore;
using UnitTestEx.Assertors;
using UnitTestEx.Generic;
using UnitTestEx.Json;
using Ceh = CoreEx.Http;
namespace UnitTestEx
{
/// <summary>
/// Provides extension methods to the core <see href="https://github.com/Avanade/unittestex"/>.
/// </summary>
public static class UnitTestExExtensions
{
#region IJsonSerializer
/// <summary>
/// Map (convert) the <see cref="CoreEx.Json.IJsonSerializer"/> to a <see cref="UnitTestEx.Json.IJsonSerializer"/>.
/// </summary>
/// <param name="jsonSerializer">The <see cref="CoreEx.Json.IJsonSerializer"/>.</param>
/// <returns>The <see cref="UnitTestEx.Json.IJsonSerializer"/> (see <see cref="ToUnitTestExJsonSerializerMapper"/>).</returns>
public static IJsonSerializer ToUnitTestEx(this CoreEx.Json.IJsonSerializer jsonSerializer) => new ToUnitTestExJsonSerializerMapper(jsonSerializer);
/// <summary>
/// Map (convert) the <see cref="UnitTestEx.Json.IJsonSerializer"/> to a <see cref="CoreEx.Json.IJsonSerializer"/>.
/// </summary>
/// <param name="jsonSerializer">The <see cref="UnitTestEx.Json.IJsonSerializer"/>.</param>
/// <returns>The <see cref="CoreEx.Json.IJsonSerializer"/> (see <see cref="ToCoreExJsonSerializerMapper"/>).</returns>
public static CoreEx.Json.IJsonSerializer ToCoreEx(this IJsonSerializer jsonSerializer) => new ToCoreExJsonSerializerMapper(jsonSerializer);
/// <summary>
/// Updates the <see cref="TesterBase.JsonSerializer"/> used by the <see cref="TesterBase{TSelf}"/> itself, not the underlying executing host which should be configured separately.
/// </summary>
/// <typeparam name="TSelf">The <typeparamref name="TSelf"/> to support fluent-style method-chaining.</typeparam>
/// <param name="tester">The <see cref="TesterBase{TSelf}"/>.</param>
/// <param name="jsonSerializer">The <see cref="JsonSerializer"/>.</param>
/// <returns>The <typeparamref name="TSelf"/> to support fluent-style method-chaining.</returns>
public static TSelf UseJsonSerializer<TSelf>(this TesterBase<TSelf> tester, CoreEx.Json.IJsonSerializer jsonSerializer) where TSelf : TesterBase<TSelf>
=> tester.UseJsonSerializer((jsonSerializer.ThrowIfNull(nameof(jsonSerializer))).ToUnitTestEx());
#endregion
#region CreateClientScope
/// <summary>
/// Creates a client-side <see cref="IServiceScope"/> from the <see cref="HttpTesterBase"/> enabling the <typeparamref name="TAgent"/>.
/// </summary>
/// <typeparam name="TAgent">The Agent (inherits from <see cref="TypedHttpClientBase"/>) <see cref="Type"/>.</typeparam>
/// <param name="tester">The <see cref="HttpTesterBase"/>.</param>
/// <returns>The <see cref="IServiceScope"/>.</returns>
public static IServiceScope CreateClientScope<TAgent>(this HttpTesterBase tester) where TAgent : TypedHttpClientBase
{
var sc = new ServiceCollection();
sc.AddExecutionContext(sp => new CoreEx.ExecutionContext { UserName = tester.UserName ?? tester.Owner.SetUp.DefaultUserName });
if (tester.JsonSerializer is CoreEx.Json.IJsonSerializer cjs)
sc.AddSingleton(cjs);
else
throw new InvalidOperationException($"The {nameof(HttpTesterBase)} must use a {nameof(IJsonSerializer)} that implements CoreEx.Json.IJsonSerializer to leverage Agent {typeof(TAgent).Name}.");
sc.AddLogging(lb => { lb.SetMinimumLevel(tester.Owner.SetUp.MinimumLogLevel); lb.ClearProviders(); lb.AddProvider(tester.Owner.LoggerProvider); });
sc.AddSingleton(new HttpClient(new HttpTesterBase.HttpDelegatingHandler(tester, tester.TestServer.CreateHandler())) { BaseAddress = tester.TestServer.BaseAddress });
sc.AddSingleton(tester.Owner.SharedState);
sc.AddSingleton(tester.Owner.Configuration);
sc.AddDefaultSettings();
sc.AddScoped<TAgent>();
return sc.BuildServiceProvider().CreateScope();
}
#endregion
#region ActionResultAssertor
/// <summary>
/// Asserts that the <see cref="ValueContentResult.ETag"/> matches the <paramref name="expectedETag"/>.
/// </summary>
/// <param name="assertor">The assertor.</param>
/// <param name="expectedETag">The expected ETag value.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
public static ActionResultAssertor AssertETagHeader(this ActionResultAssertor assertor, string expectedETag)
{
if (assertor.Result != null && assertor.Result is ValueContentResult vcr)
assertor.Owner.Implementor.AssertAreEqual(expectedETag, vcr.ETag, $"Expected and Actual {nameof(ValueContentResult.ETag)} values are not equal.");
else
assertor.Owner.Implementor.AssertFail($"The Result must be of Type {typeof(ValueContentResult).FullName} to use {nameof(AssertETagHeader)}.");
return assertor;
}
/// <summary>
/// Asserts that the <see cref="ValueContentResult.Location"/> matches the <paramref name="expectedUri"/>.
/// </summary>
/// <param name="assertor">The assertor.</param>
/// <param name="expectedUri">The expected <see cref="Uri"/>.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
#if NET7_0_OR_GREATER
public static ActionResultAssertor AssertLocationHeader(this ActionResultAssertor assertor, [StringSyntax(StringSyntaxAttribute.Uri)] Uri expectedUri)
#else
public static ActionResultAssertor AssertLocationHeader(this ActionResultAssertor assertor, Uri expectedUri)
#endif
{
if (assertor.Result != null && assertor.Result is ValueContentResult vcr)
assertor.Owner.Implementor.AssertAreEqual(expectedUri, vcr.Location, $"Expected and Actual {nameof(ValueContentResult.Location)} values are not equal.");
else if (assertor.Result != null && assertor.Result is ExtendedStatusCodeResult escr)
assertor.Owner.Implementor.AssertAreEqual(expectedUri, escr.Location, $"Expected and Actual {nameof(ExtendedStatusCodeResult.Location)} values are not equal.");
else
assertor.Owner.Implementor.AssertFail($"The Result must be of Type {typeof(ValueContentResult).FullName} or {typeof(ExtendedStatusCodeResult).FullName} to use {nameof(AssertLocationHeader)}.");
return assertor;
}
/// <summary>
/// Asserts that the <see cref="ValueContentResult.Location"/> matches the <paramref name="expectedUri"/> function.
/// </summary>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="assertor">The assertor.</param>
/// <param name="expectedUri">The expected <see cref="Uri"/> function.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
public static ActionResultAssertor AssertLocationHeader<TValue>(this ActionResultAssertor assertor, Func<TValue, Uri> expectedUri)
=> assertor.AssertLocationHeader(expectedUri.Invoke(assertor.GetValue<TValue>()!));
/// <summary>
/// Asserts that the <see cref="ValueContentResult.Location"/> contains the <paramref name="expected"/> string.
/// </summary>
/// <param name="assertor">The assertor.</param>
/// <param name="expected">The expected string.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
public static ActionResultAssertor AssertLocationHeaderContains(this ActionResultAssertor assertor, string expected)
{
Uri? actual = null;
if (assertor.Result != null && assertor.Result is ValueContentResult vcr)
actual = vcr.Location;
else if (assertor.Result != null && assertor.Result is ExtendedStatusCodeResult escr)
actual = escr.Location;
else
assertor.Owner.Implementor.AssertFail($"The Result must be of Type {typeof(ValueContentResult).FullName} or {typeof(ExtendedStatusCodeResult).FullName} to use {nameof(AssertLocationHeader)}.");
if (actual == null)
assertor.Owner.Implementor.AssertFail($"The actual {nameof(ValueContentResult.Location)} must not be null.");
if (!actual!.ToString().Contains(expected))
assertor.Owner.Implementor.AssertFail($"The {nameof(ValueContentResult.Location)} '{actual}' must contain {expected}.");
return assertor;
}
/// <summary>
/// Asserts that the <see cref="ValueContentResult.Location"/> contains the <paramref name="expected"/> string function.
/// </summary>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="assertor">The assertor.</param>
/// <param name="expected">The expected string function.</param>
/// <returns>The <see cref="ActionResultAssertor"/> to support fluent-style method-chaining.</returns>
public static ActionResultAssertor AssertLocationHeader<TValue>(this ActionResultAssertor assertor, Func<TValue, string> expected)
=> assertor.AssertLocationHeaderContains(expected.Invoke(assertor.GetValue<TValue>()!));
#endregion
#region GenericTesterBase
/// <summary>
/// Enables the validation <see cref="GenericTesterBaseWith{TEntryPoint, TSelf}">with</see> a specified validation.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TSelf">The <see cref="ApiTesterBase{TEntryPoint, TSelf}"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <param name="operationType">The optional <see cref="OperationType"/> for the test (updates the <see cref="ExecutionContext.Current"/>).</param>
public static GenericTesterBaseWith<TEntryPoint, TSelf> Validation<TEntryPoint, TSelf>(this GenericTesterBase<TEntryPoint, TSelf> tester, OperationType operationType = OperationType.Unspecified) where TEntryPoint : class, new() where TSelf : GenericTesterBase<TEntryPoint, TSelf>
=> new(tester, operationType);
/// <summary>
/// Enables the validation.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TSelf">The <see cref="ApiTesterBase{TEntryPoint, TSelf}"/>.</typeparam>
public class GenericTesterBaseWith<TEntryPoint, TSelf> where TEntryPoint : class, new() where TSelf : GenericTesterBase<TEntryPoint, TSelf>
{
private readonly GenericTesterBase<TEntryPoint, TSelf> _tester;
private readonly OperationType _operationType;
/// <summary>
/// Initializes a new instance of the <see cref="AgentTesterWith{TEntryPoint, TSelf}"/>.
/// </summary>
internal GenericTesterBaseWith(GenericTesterBase<TEntryPoint, TSelf> tester, OperationType operationType)
{
_tester = tester.ThrowIfNull(nameof(tester));
_operationType = operationType;
}
/// <summary>
/// Creates (instantiates) the <typeparamref name="TValidator"/> using Dependency Injection (DI) and validates the <typeparamref name="TValue"/> <paramref name="value"/>.
/// </summary>
/// <typeparam name="TValidator">The validator <see cref="Type"/>.</typeparam>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="value">The value to validate.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public ValueAssertor<IValidationResult> With<TValidator, TValue>(TValue value) where TValue : class where TValidator : class, IValidator<TValue>
=> WithAsync<TValidator, TValue>(value).GetAwaiter().GetResult();
/// <summary>
/// Validates the <typeparamref name="TValue"/> <paramref name="value"/> using the <paramref name="validator"/>.
/// </summary>
/// <typeparam name="TValidator">The validator <see cref="Type"/>.</typeparam>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="validator">The validator.</param>
/// <param name="value">The value to validate.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public ValueAssertor<IValidationResult> With<TValidator, TValue>(TValidator validator, TValue value) where TValue : class where TValidator : class, IValidator<TValue>
=> WithAsync(validator, value).GetAwaiter().GetResult();
/// <summary>
/// Executes the <paramref name="validation"/> function.
/// </summary>
/// <param name="validation">The validation function.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public ValueAssertor<IValidationResult> With(Func<IValidationResult> validation) => WithAsync(() => Task.FromResult(validation())).GetAwaiter().GetResult();
/// <summary>
/// Executes the <paramref name="validation"/> function.
/// </summary>
/// <param name="validation">The validation function.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public ValueAssertor<IValidationResult> With(Func<Task<IValidationResult>> validation) => WithAsync(validation).GetAwaiter().GetResult();
/// <summary>
/// Creates (instantiates) the <typeparamref name="TValidator"/> using Dependency Injection (DI) and validates the <typeparamref name="TValue"/> <paramref name="value"/>.
/// </summary>
/// <typeparam name="TValidator">The validator <see cref="Type"/>.</typeparam>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="value">The value to validate.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public Task<ValueAssertor<IValidationResult>> WithAsync<TValidator, TValue>(TValue value) where TValue : class where TValidator : class, IValidator<TValue>
=> WithAsync(_tester.Services.GetService<TValidator>() ?? throw new InvalidOperationException($"Validator '{typeof(TValidator).FullName}' not configured using Dependency Injection (DI) and therefore unable to be instantiated for testing."), value);
/// <summary>
/// Validates the <typeparamref name="TValue"/> <paramref name="value"/> using the <paramref name="validator"/>.
/// </summary>
/// <typeparam name="TValidator">The validator <see cref="Type"/>.</typeparam>
/// <typeparam name="TValue">The value <see cref="Type"/>.</typeparam>
/// <param name="validator">The validator.</param>
/// <param name="value">The value to validate.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public Task<ValueAssertor<IValidationResult>> WithAsync<TValidator, TValue>(TValidator validator, TValue value) where TValue : class where TValidator : class, IValidator<TValue>
=> WithAsync(async () => await validator.ThrowIfNull(nameof(validator)).ValidateAsync(value).ConfigureAwait(false));
/// <summary>
/// Executes the <paramref name="validation"/> function.
/// </summary>
/// <param name="validation">The validation function.</param>
/// <returns>The <see cref="ValueAssertor{TValue}"/> with the resulting <see cref="IValidationResult"/>.</returns>
public Task<ValueAssertor<IValidationResult>> WithAsync(Func<Task<IValidationResult>> validation)
=> _tester.RunAsync(async () =>
{
// Build out an execution context.
OperationType? existingOperationType = null;
if (ExecutionContext.HasCurrent)
existingOperationType = ExecutionContext.Current.OperationType;
else
{
var ec = _tester.Services.GetService<ExecutionContext>();
if (ec is null)
_ = ExecutionContext.Current;
else if (!ExecutionContext.HasCurrent)
ExecutionContext.SetCurrent(ec);
// Update service provider where null.
ExecutionContext.Current.ServiceProvider ??= _tester.Services;
}
// Set/override the operation type.
ExecutionContext.Current.OperationType = _operationType;
// Perform the validation.
try
{
var vr = await validation().ConfigureAwait(false);
vr.ThrowOnError();
return vr;
}
finally
{
// Reset where finished.
if (existingOperationType.HasValue)
ExecutionContext.Current.OperationType = existingOperationType.Value;
else
ExecutionContext.Reset();
}
});
}
#endregion
#region ApiTesterBase
/// <summary>
/// Enables a test <see cref="HttpRequestMessage"/> to be sent to the underlying <see cref="TestServer"/> <see cref="AgentTesterWith{TEntryPoint, TSelf}">with</see> a specified <see cref="TypedHttpClientBase">agent</see>.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TSelf">The <see cref="ApiTesterBase{TEntryPoint, TSelf}"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <returns>The <see cref="AgentTesterWith{TEntryPoint, TSelf}"/> to allow <b>Agent</b> type specification.</returns>
public static AgentTesterWith<TEntryPoint, TSelf> Agent<TEntryPoint, TSelf>(this ApiTesterBase<TEntryPoint, TSelf> tester)
where TEntryPoint : class where TSelf : ApiTesterBase<TEntryPoint, TSelf>
=> new(tester);
/// <summary>
/// Enables the <see cref="TypedHttpClientBase">agent</see> <see cref="Type"/> specification.
/// </summary>
/// <typeparam name="TEntryPoint">The API startup <see cref="Type"/>.</typeparam>
/// <typeparam name="TSelf">The <see cref="ApiTesterBase{TEntryPoint, TSelf}"/>.</typeparam>
public class AgentTesterWith<TEntryPoint, TSelf> where TEntryPoint : class where TSelf : ApiTesterBase<TEntryPoint, TSelf>
{
private readonly ApiTesterBase<TEntryPoint, TSelf> _tester;
/// <summary>
/// Initializes a new instance of the <see cref="AgentTesterWith{TEntryPoint, TSelf}"/>.
/// </summary>
internal AgentTesterWith(ApiTesterBase<TEntryPoint, TSelf> tester) => _tester = tester.ThrowIfNull(nameof(tester));
/// <summary>
/// Enables a test <see cref="HttpRequestMessage"/> to be sent to the underlying <see cref="TestServer"/> leveraging the specified <see cref="TypedHttpClientBase">agent</see>.
/// </summary>
/// <typeparam name="TAgent">The <see cref="AgentTester{TAgent}"/>.</typeparam>
/// <returns>The <see cref="AgentTester{TAgent}"/>.</returns>
public AgentTester<TAgent> With<TAgent>() where TAgent : TypedHttpClientBase => new(_tester, _tester.GetTestServer());
/// <summary>
/// Enables a test <see cref="HttpRequestMessage"/> to be sent to the underlying <see cref="TestServer"/> leveraging the specified <see cref="TypedHttpClientBase">agent</see>.
/// </summary>
/// <typeparam name="TAgent">The <see cref="AgentTester{TAgent}"/>.</typeparam>
/// <typeparam name="TValue">The response value <see cref="Type"/>.</typeparam>
/// <returns>The <see cref="AgentTester{TAgent, TValue}"/>.</returns>
public AgentTester<TAgent, TValue> With<TAgent, TValue>() where TAgent : TypedHttpClientBase => new(_tester, _tester.GetTestServer());
}
#endregion
#region ControllerTester
/// <summary>
/// Runs the controller using an <see cref="HttpRequestMessage"/> inferring the <see cref="HttpMethod"/>, operation name and request from the <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TController">The <see cref="ControllerBase"/> <see cref="Type"/>.</typeparam>
/// <typeparam name="TResult">The result value <see cref="Type"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <param name="expression">The controller operation invocation expression.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequestMessage"/> modifier.</param>
/// <returns>A <see cref="HttpResponseMessageAssertor"/>.</returns>
public static HttpResponseMessageAssertor Run<TController, TResult>(this ControllerTester<TController> tester, Expression<Func<TController, TResult>> expression, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequestMessage>? requestModifier = null)
where TController : ControllerBase
=> RunAsync(tester, expression, requestOptions, requestModifier).GetAwaiter().GetResult();
/// <summary>
/// Runs the controller using an <see cref="HttpRequestMessage"/> inferring the <see cref="HttpMethod"/>, operation name and request from the <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TController">The <see cref="ControllerBase"/> <see cref="Type"/>.</typeparam>
/// <typeparam name="TResult">The result value <see cref="Type"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <param name="expression">The controller operation invocation expression.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequestMessage"/> modifier.</param>
/// <returns>A <see cref="HttpResponseMessageAssertor"/>.</returns>
public static Task<HttpResponseMessageAssertor> RunAsync<TController, TResult>(this ControllerTester<TController> tester, Expression<Func<TController, TResult>> expression, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequestMessage>? requestModifier = null)
where TController : ControllerBase
{
void rm(HttpRequestMessage hr)
{
requestModifier?.Invoke(hr);
hr.ApplyRequestOptions(requestOptions);
}
return tester.RunAsync(expression, rm);
}
/// <summary>
/// Runs the controller using an <see cref="HttpRequestMessage"/> inferring the <see cref="HttpMethod"/>, operation name and request from the <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TController">The <see cref="ControllerBase"/> <see cref="Type"/>.</typeparam>
/// <typeparam name="TResult">The result value <see cref="Type"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <param name="expression">The controller operation invocation expression.</param>
/// <param name="content">The body content.</param>
/// <param name="contentType">The body content type. Defaults to <see cref="MediaTypeNames.Text.Plain"/>.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequestMessage"/> modifier.</param>
/// <returns>A <see cref="HttpResponseMessageAssertor"/>.</returns>
public static HttpResponseMessageAssertor RunContent<TController, TResult>(this ControllerTester<TController> tester, Expression<Func<TController, TResult>> expression, string? content, string? contentType = MediaTypeNames.Text.Plain, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequestMessage>? requestModifier = null)
where TController : ControllerBase
=> RunContentAsync(tester, expression, content, contentType, requestOptions, requestModifier).GetAwaiter().GetResult();
/// <summary>
/// Runs the controller using an <see cref="HttpRequestMessage"/> inferring the <see cref="HttpMethod"/>, operation name and request from the <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TController">The <see cref="ControllerBase"/> <see cref="Type"/>.</typeparam>
/// <typeparam name="TResult">The result value <see cref="Type"/>.</typeparam>
/// <param name="tester">The tester.</param>
/// <param name="expression">The controller operation invocation expression.</param>
/// <param name="content">The body content.</param>
/// <param name="contentType">The body content type. Defaults to <see cref="MediaTypeNames.Text.Plain"/>.</param>
/// <param name="requestOptions">The optional <see cref="Ceh.HttpRequestOptions"/>.</param>
/// <param name="requestModifier">The optional <see cref="HttpRequestMessage"/> modifier.</param>
/// <returns>A <see cref="HttpResponseMessageAssertor"/>.</returns>
public static Task<HttpResponseMessageAssertor> RunContentAsync<TController, TResult>(this ControllerTester<TController> tester, Expression<Func<TController, TResult>> expression, string? content, string? contentType = MediaTypeNames.Text.Plain, Ceh.HttpRequestOptions? requestOptions = null, Action<HttpRequestMessage>? requestModifier = null)
where TController : ControllerBase
{
void rm(HttpRequestMessage hr)
{
requestModifier?.Invoke(hr);
hr.ApplyRequestOptions(requestOptions);
}
return tester.RunContentAsync(expression, content, contentType, rm);
}
#endregion
}
}