22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
44using System . Collections . Generic ;
5- using System . Diagnostics ;
65using System . Linq ;
76using System . Net ;
87using System . Net . Http ;
1716
1817namespace Microsoft . AspNetCore . Mvc . FunctionalTests
1918{
20- public class ApiBehaviorTest : IClassFixture < MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > >
19+ public abstract class ApiBehaviorTestBase < TStartup > : IClassFixture < MvcTestFixture < TStartup > > where TStartup : class
2120 {
22- public ApiBehaviorTest ( MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > fixture )
21+ protected ApiBehaviorTestBase ( MvcTestFixture < TStartup > fixture )
2322 {
24- Client = fixture . CreateDefaultClient ( ) ;
25-
26- var factory = fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
27- CustomInvalidModelStateClient = factory . CreateDefaultClient ( ) ;
23+ var factory = fixture . Factories . FirstOrDefault ( ) ?? fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
24+ Client = factory . CreateDefaultClient ( ) ;
2825 }
2926
3027 private static void ConfigureWebHostBuilder ( IWebHostBuilder builder ) =>
31- builder . UseStartup < BasicWebSite . StartupWithCustomInvalidModelStateFactory > ( ) ;
28+ builder . UseStartup < TStartup > ( ) ;
3229
3330 public HttpClient Client { get ; }
34- public HttpClient CustomInvalidModelStateClient { get ; }
3531
3632 [ Fact ]
37- public async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
33+ public virtual async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
3834 {
3935 // Arrange
4036 using ( new ActivityReplacer ( ) )
@@ -122,34 +118,6 @@ public async Task ActionsReturnUnsupportedMediaType_WhenEncodingIsUnsupported()
122118 Assert . Equal ( "Unsupported Media Type" , problemDetails . Title ) ;
123119 }
124120
125- [ Fact ]
126- public async Task ActionsReturnBadRequest_UsesProblemDescriptionProviderAndApiConventionsToConfigureErrorResponse ( )
127- {
128- // Arrange
129- var contactModel = new Contact
130- {
131- Name = "Abc" ,
132- City = "Redmond" ,
133- State = "WA" ,
134- Zip = "Invalid" ,
135- } ;
136- var expected = new Dictionary < string , string [ ] >
137- {
138- { "Name" , new [ ] { "The field Name must be a string with a minimum length of 5 and a maximum length of 30." } } ,
139- { "Zip" , new [ ] { @"The field Zip must match the regular expression '\d{5}'." } }
140- } ;
141-
142- // Act
143- var response = await CustomInvalidModelStateClient . PostAsJsonAsync ( "/contact/PostWithVnd" , contactModel ) ;
144-
145- // Assert
146- await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
147- Assert . Equal ( "application/vnd.error+json" , response . Content . Headers . ContentType . MediaType ) ;
148- var content = await response . Content . ReadAsStringAsync ( ) ;
149- var actual = JsonConvert . DeserializeObject < Dictionary < string , string [ ] > > ( content ) ;
150- Assert . Equal ( expected , actual ) ;
151- }
152-
153121 [ Fact ]
154122 public Task ActionsWithApiBehavior_InferFromBodyParameters ( )
155123 => ActionsWithApiBehaviorInferFromBodyParameters ( "ActionWithInferredFromBodyParameter" ) ;
@@ -171,7 +139,7 @@ private async Task ActionsWithApiBehaviorInferFromBodyParameters(string action)
171139 var response = await Client . PostAsJsonAsync ( $ "/contact/{ action } ", input ) ;
172140
173141 // Assert
174- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
142+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
175143 var result = JsonConvert . DeserializeObject < Contact > ( await response . Content . ReadAsStringAsync ( ) ) ;
176144 Assert . Equal ( input . ContactId , result . ContactId ) ;
177145 Assert . Equal ( input . Name , result . Name ) ;
@@ -188,7 +156,7 @@ public async Task ActionsWithApiBehavior_InferQueryAndRouteParameters()
188156 var response = await Client . PostAsync ( url , new StringContent ( string . Empty ) ) ;
189157
190158 // Assert
191- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
159+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
192160 var result = JsonConvert . DeserializeObject < Contact > ( await response . Content . ReadAsStringAsync ( ) ) ;
193161 Assert . Equal ( id , result . ContactId ) ;
194162 Assert . Equal ( name , result . Name ) ;
@@ -208,7 +176,7 @@ public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProvider
208176 var response = await Client . GetAsync ( url ) ;
209177
210178 // Assert
211- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
179+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
212180
213181 var result = await response . Content . ReadAsAsync < Contact > ( ) ;
214182 Assert . Equal ( id , result . ContactId ) ;
@@ -229,7 +197,7 @@ public async Task ActionsWithApiBehavior_InferEmptyPrefixForComplexValueProvider
229197 var response = await Client . GetAsync ( url ) ;
230198
231199 // Assert
232- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
200+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
233201
234202 var result = await response . Content . ReadAsAsync < Contact > ( ) ;
235203 Assert . Equal ( id , result . ContactId ) ;
@@ -247,7 +215,7 @@ public async Task ActionsWithApiBehavior_InferModelBinderType()
247215 var response = await Client . GetAsync ( "/contact/ActionWithInferredModelBinderType?foo=Hello!" ) ;
248216
249217 // Assert
250- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
218+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
251219 var result = await response . Content . ReadAsStringAsync ( ) ;
252220 Assert . Equal ( expected , result ) ;
253221 }
@@ -262,13 +230,13 @@ public async Task ActionsWithApiBehavior_InferModelBinderTypeWithExplicitModelNa
262230 var response = await Client . GetAsync ( "/contact/ActionWithInferredModelBinderTypeWithExplicitModelName?bar=Hello!" ) ;
263231
264232 // Assert
265- Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
233+ await response . AssertStatusCodeAsync ( HttpStatusCode . OK ) ;
266234 var result = await response . Content . ReadAsStringAsync ( ) ;
267235 Assert . Equal ( expected , result ) ;
268236 }
269237
270238 [ Fact ]
271- public async Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
239+ public virtual async Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
272240 {
273241 using ( new ActivityReplacer ( ) )
274242 {
@@ -296,7 +264,7 @@ public async Task ClientErrorResultFilterExecutesForStatusCodeResults()
296264 }
297265
298266 [ Fact ]
299- public async Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
267+ public virtual async Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
300268 {
301269 // Arrange
302270 var expected = new [ ] { "status" , "title" , "traceId" , "type" } ;
@@ -314,7 +282,7 @@ public async Task SerializingProblemDetails_IgnoresNullValuedProperties()
314282 }
315283
316284 [ Fact ]
317- public async Task SerializingProblemDetails_WithAllValuesSpecified ( )
285+ public virtual async Task SerializingProblemDetails_WithAllValuesSpecified ( )
318286 {
319287 // Arrange
320288 var expected = new [ ] { "detail" , "instance" , "status" , "title" , "tracking-id" , "type" } ;
@@ -330,7 +298,7 @@ public async Task SerializingProblemDetails_WithAllValuesSpecified()
330298 }
331299
332300 [ Fact ]
333- public async Task SerializingValidationProblemDetails_WithExtensionData ( )
301+ public virtual async Task SerializingValidationProblemDetails_WithExtensionData ( )
334302 {
335303 // Act
336304 var response = await Client . GetAsync ( "/contact/ActionReturningValidationProblemDetails" ) ;
@@ -364,4 +332,132 @@ public async Task SerializingValidationProblemDetails_WithExtensionData()
364332 } ) ;
365333 }
366334 }
335+
336+ public class ApiBehaviorTest : ApiBehaviorTestBase < BasicWebSite . StartupWithSystemTextJson >
337+ {
338+ public ApiBehaviorTest ( MvcTestFixture < BasicWebSite . StartupWithSystemTextJson > fixture )
339+ : base ( fixture )
340+ {
341+ }
342+
343+ [ Fact ]
344+ public override async Task ActionsReturnBadRequest_WhenModelStateIsInvalid ( )
345+ {
346+ // Arrange
347+ using var _ = new ActivityReplacer ( ) ;
348+
349+ var contactModel = new Contact
350+ {
351+ Name = "Abc" ,
352+ City = "Redmond" ,
353+ State = "WA" ,
354+ Zip = "Invalid" ,
355+ } ;
356+ var contactString = JsonConvert . SerializeObject ( contactModel ) ;
357+
358+ // Act
359+ var response = await Client . PostAsJsonAsync ( "/contact" , contactModel ) ;
360+
361+ // Assert
362+ await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
363+ Assert . Equal ( "application/problem+json" , response . Content . Headers . ContentType . MediaType ) ;
364+ var problemDetails = JsonConvert . DeserializeObject < ValidationProblemDetails > (
365+ await response . Content . ReadAsStringAsync ( ) ,
366+ new JsonSerializerSettings
367+ {
368+ Converters = { new ValidationProblemDetailsConverter ( ) }
369+ } ) ;
370+ Assert . Collection (
371+ problemDetails . Errors . OrderBy ( kvp => kvp . Key ) ,
372+ kvp =>
373+ {
374+ Assert . Equal ( "Name" , kvp . Key ) ;
375+ var error = Assert . Single ( kvp . Value ) ;
376+ Assert . Equal ( "The field Name must be a string with a minimum length of 5 and a maximum length of 30." , error ) ;
377+ } ,
378+ kvp =>
379+ {
380+ Assert . Equal ( "Zip" , kvp . Key ) ;
381+ var error = Assert . Single ( kvp . Value ) ;
382+ Assert . Equal ( "The field Zip must match the regular expression '\\ d{5}'." , error ) ;
383+ }
384+ ) ;
385+
386+ Assert . Collection (
387+ problemDetails . Extensions ,
388+ kvp =>
389+ {
390+ Assert . Equal ( "extensions" , kvp . Key ) ;
391+ var jObject = Assert . IsType < JObject > ( kvp . Value ) ;
392+ Assert . Equal ( "traceId" , Assert . Single ( jObject . Properties ( ) ) . Name ) ;
393+ } ) ;
394+ }
395+
396+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
397+ public override Task ClientErrorResultFilterExecutesForStatusCodeResults ( )
398+ {
399+ return base . ClientErrorResultFilterExecutesForStatusCodeResults ( ) ;
400+ }
401+
402+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
403+ public override Task SerializingProblemDetails_IgnoresNullValuedProperties ( )
404+ {
405+ return base . SerializingProblemDetails_IgnoresNullValuedProperties ( ) ;
406+ }
407+
408+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
409+ public override Task SerializingProblemDetails_WithAllValuesSpecified ( )
410+ {
411+ return base . SerializingProblemDetails_WithAllValuesSpecified ( ) ;
412+ }
413+
414+ [ Fact ( Skip = "https://github.com/dotnet/corefx/issues/38769" ) ]
415+ public override Task SerializingValidationProblemDetails_WithExtensionData ( )
416+ {
417+ return base . SerializingValidationProblemDetails_WithExtensionData ( ) ;
418+ }
419+ }
420+
421+ public class ApiBehaviorTestNewtonsoftJson : ApiBehaviorTestBase < BasicWebSite . StartupWithoutEndpointRouting >
422+ {
423+ public ApiBehaviorTestNewtonsoftJson ( MvcTestFixture < BasicWebSite . StartupWithoutEndpointRouting > fixture )
424+ : base ( fixture )
425+ {
426+ var factory = fixture . WithWebHostBuilder ( ConfigureWebHostBuilder ) ;
427+ CustomInvalidModelStateClient = factory . CreateDefaultClient ( ) ;
428+ }
429+
430+ private static void ConfigureWebHostBuilder ( IWebHostBuilder builder ) =>
431+ builder . UseStartup < BasicWebSite . StartupWithCustomInvalidModelStateFactory > ( ) ;
432+
433+ public HttpClient CustomInvalidModelStateClient { get ; }
434+
435+ [ Fact ]
436+ public async Task ActionsReturnBadRequest_UsesProblemDescriptionProviderAndApiConventionsToConfigureErrorResponse ( )
437+ {
438+ // Arrange
439+ var contactModel = new Contact
440+ {
441+ Name = "Abc" ,
442+ City = "Redmond" ,
443+ State = "WA" ,
444+ Zip = "Invalid" ,
445+ } ;
446+ var expected = new Dictionary < string , string [ ] >
447+ {
448+ { "Name" , new [ ] { "The field Name must be a string with a minimum length of 5 and a maximum length of 30." } } ,
449+ { "Zip" , new [ ] { @"The field Zip must match the regular expression '\d{5}'." } }
450+ } ;
451+
452+ // Act
453+ var response = await CustomInvalidModelStateClient . PostAsJsonAsync ( "/contact/PostWithVnd" , contactModel ) ;
454+
455+ // Assert
456+ await response . AssertStatusCodeAsync ( HttpStatusCode . BadRequest ) ;
457+ Assert . Equal ( "application/vnd.error+json" , response . Content . Headers . ContentType . MediaType ) ;
458+ var content = await response . Content . ReadAsStringAsync ( ) ;
459+ var actual = JsonConvert . DeserializeObject < Dictionary < string , string [ ] > > ( content ) ;
460+ Assert . Equal ( expected , actual ) ;
461+ }
462+ }
367463}
0 commit comments