adrianiftode / fluentassertions.web Goto Github PK
View Code? Open in Web Editor NEWFluentAssertions for HTTP APIs
License: Apache License 2.0
FluentAssertions for HTTP APIs
License: Apache License 2.0
Instead of Expected response to have a content equivalent to a model, but is has differences: it should be , but it is has
Firstly, thanks for your work on the library - has made testing web APIs very easy.
I've encountered an issue with logs when sending binary requests. I'm testing an image upload endpoint and in my test I'm uploading a high resolution jpg file which is 900kb (testing that the server downscales large images to a specified max resolution correctly and such before storing the file).
When the test passes everything is good, but if it fails,
And VS as a whole is largely unusable unless you change the panel to something other than the test explorer.
If you go to a test in code, click on "Show in test explorer", then wait out the 20+ sec, it then becomes possible to read the log message (as per my point 1) to try to resolve it to get the test to pass. But yeah - if the log can be improved to not show binary data or perhaps show it as a hex string, this might help the problem, or perhaps it's to do with how it tries to find the point to truncate the body that has an issue when reading binary.
I have attached a sample project which reproduces the issue. If you go into the test project, under /Controllers/MyApiControllerTests.cs
, the first test should return 200 OK but I've made the test assert for 400 bad request just to force the test to fail - you can comment this and uncomment the check for 200 OK to get the test to pass. I also added a 2nd test just to demonstrate that VS stops drawing tests below as per screenshot above, but I realise this is to do with the VS GUI and not the library.
Let me know if there is any more information I can provide.
Ie
Consider adding a test that scans the types withing the Internal namespace and assert their access modifier to be internal or less
In this example, the header actually exists, but the error message suggests it doesn't. A better message would express the fact the value differs.
Expected value to contain the HTTP header "Location" having value "/api/iab/optout", but no such header was found in the actual response.
The HTTP response was:
HTTP/1.1 302 Redirect
Vary: Cookie
Location: /api/iab/status
Set-Cookie: XSRF-TOKEN=Fpxq1CaquEChrslAeZ37TAa; path=/
Content-Length: 0
***** Content is of a stream type having the length 0. *****
The originated HTTP request was:
GET https://.../api/iab/coop?action=3 HTTP 2.0
Cookie:
Content-Length: 0
Hi.
The current version of the package does not work with FluentAssertions 6.0.0, recently released.
Be401Unauthorized()
, for example, raised MissingMethodException
.
Given a test using the following snippet
// Act
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent("", Encoding.UTF8)
};
response.Content.Headers.ContentType = null;
// Assert
response.Should().Be200Ok();
When it is run
Then the expected behavior is to fail with the following message
Message:
Expected HTTP response to be "OK", but found Unauthorized.
The HTTP response was:
HTTP/1.1 401 Unauthorized
Content-Length: 0
The originated HTTP request was <null>.
But the actual behavior is it fails with the following message
```Message:
System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
JsonProcessor.CanHandle()
JsonProcessor.GetContentInfo(StringBuilder contentBuilder)
HttpResponseMessageFormatter.RunProcessors(IContentProcessor[] processors)
HttpResponseMessageFormatter.AppendResponseContent(StringBuilder messageBuilder, HttpResponseMessage response)
HttpResponseMessageFormatter.AppendResponse(StringBuilder messageBuilder, HttpResponseMessage response)
HttpResponseMessageFormatter.AppendHttpResponseMessage(StringBuilder messageBuilder, HttpResponseMessage response)
<<Format>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
HttpResponseMessageFormatter.Format(Object value, FormattingContext context, FormatChild formatChild)
Formatter.ToString(Object value, Boolean useLineBreaks) line 97
SelectArrayIterator`2.ToArray()
Enumerable.ToArray[TSource](IEnumerable`1 source)
MessageBuilder.FormatArgumentPlaceholders(String failureMessage, Object[] failureArgs) line 91
MessageBuilder.Build(String message, Object[] messageArgs, String reason, ContextDataItems contextData, String identifier, String fallbackIdentifier) line 36
<>c__DisplayClass30_0.<FailWith>b__0() line 199
AssertionScope.FailWith(Func`1 failReasonFunc) line 216
HttpResponseMessageAssertions.ExecuteStatusAssertion(String because, Object[] becauseArgs, HttpStatusCode expected, String otherName)
HttpResponseMessageAssertions.Be200Ok(String because, Object[] becauseArgs)
TestCasesTests.Get_TestCases_ShouldBeOk() line 39
--- End of stack trace from previous location where exception was thrown ---
After FluentAssertions 6.4.0 release where fluentassertions/fluentassertions#1737 was merged, this library now conflicts with the main one, so I have a bunch of errors like
OrderTests.cs(45,18): error CS0121: The call is ambiguous between the following methods or properties: 'FluentAssertions.AssertionExtensions.Should(System.Net.Http.HttpResponseMessage)' and 'FluentAssertions.HttpResponseMessageFluentAssertionsExtensions.Should(System.Net.Http.HttpResponseMessage?)'
I have no idea how I can resolve this manually.
Do you know a way to use the latest FluentAssertions and FluentAssertions.Web?
Steps
response.Should().Be500InternalServerError()
.And.HaveContent("An account with email `xxx` already exists");
Expected
The test to pass or the test to fails with the correct assertions messages
Actual outcome
Message:
Newtonsoft.Json.JsonReaderException : Unexpected character encountered while parsing value: <. Path '', line 1, position 1.
Stack Trace:
JsonTextReader.ReadStringValue(ReadType readType)
JsonTextReader.ReadAsString()
JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
HttpContentExtensions.ReadAsAsync[T](HttpContent content)
HttpResponseMessageAssertions.GetSubjectModel[TModel]()
HttpResponseMessageAssertions.HaveContent[TModel](TModel expectedModel, String because, Object[] becauseArgs)
UsersControllerTests.Post_ReturnsServerError_WhenUserDoesNotExist_ButAPhantomAccountDoes() line 80
--- End of stack trace from previous location where exception was thrown ---
Currently to check that redirect response that has the expected redirect location something like the following is tried:
response.Should().Be302Redirect()
.And.HaveHeader("Location")
.And.Match("/redirect-location");
What could ease would be:
to assert an exact match of the redirect header
response.Should().Be302Redirect()
.And.HaveLocation("/redirect-location?id=231");
or to have a wild card match
response.Should().Be302Redirect()
.And.MatchLocation("/redirect-location");
The expected model was a single object, and the response an array.
Expected response to have a content equivalent to a model of type "<>f__AnonymousType0`7[System.Int32,System.String,System.String,System.Int32,System.DateTime,System.Int32,System.Int32]", but the JSON representation could not be parsed, as the operation failed with the following message: "Exception while deserializing the model with SystemTextJsonSerializer: The JSON value could not be converted to <>f__AnonymousType0`7[System.Int32,System.String,System.String,System.Int32,System.DateTime,System.Int32,System.Int32]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.".
The HTTP response was:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 2025
***** Content is too large to display and only a part is printed. *****
[{"id":"03e9bf9d-5fec-4cdc-82a3-19c5fa1c847e","userName":"Utility",
And check the response formatting
C:\Program Files\dotnet\sdk\7.0.100\Microsoft.Common.CurrentVersion.targets(2352,5): warning MSB3277: Found conflicts between different versions of "System.T ext.Encodings.Web" that could not be resolved. [D:\adrian\opensource\FluentAssertions.Web\test\FluentAssertions.Web.FluentAssertionsWebConfig.Tests\FluentAss ertions.Web.FluentAssertionsWebConfig.Tests.csproj::TargetFramework=net5.0]
C:\Program Files\dotnet\sdk\7.0.100\Microsoft.Common.CurrentVersion.targets(2352,5): warning MSB3277: There was a conflict between "System.Text.Encodings.Web , Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" and "System.Text.Encodings.Web, Version=5.0.0.1, Culture=neutral, PublicKeyToken=cc7b13f fcd2ddd51". [D:\adrian\opensource\FluentAssertions.Web\test\FluentAssertions.Web.FluentAssertionsWebConfig.Tests\FluentAssertions.Web.FluentAssertionsWebConf ig.Tests.csproj::TargetFramework=net5.0]
Currently the user needs to update the tests classes so they have the using FluentAssertions.Web;
line, in order the use the discover the Web extensions.
This will be a problem in adoption for larger code bases and even for new tests
Context: a response having multiple Set-Cookie headers
Actual
The HTTP response was:
HTTP/1.1 200 OK
Vary: Cookie
Set-Cookie: uid=6cfdfc78-2200-4f7b-9c19-c38b379156a8; expires=Tue, 19 Apr 2022 18:42:10 GMT; path=/, XSRF-TOKEN=7d25c15c-c116-4859-9d2b-ecc13a032214; path=/
Content-Length: 0
***** Content is of a stream type having the length 0. *****
The originated HTTP request was:
Expected
The HTTP response was:
HTTP/1.1 200 OK
Vary: Cookie
Set-Cookie: uid=6cfdfc78-2200-4f7b-9c19-c38b379156a8; expires=Tue, 19 Apr 2022 18:42:10 GMT; path=/
Set-Cookie: XSRF-TOKEN=7d25c15c-c116-4859-9d2b-ecc13a032214; path=/
Content-Length: 0
***** Content is of a stream type having the length 0. *****
The originated HTTP request was:
While the wildcard meaning is explained like "The wildcard pattern with which the subject is matched, where * and ? have special meanings.", it worths adding some examples.
Use case:
Consider the following response:
Expected HTTP response to be "OK", but found InternalServerError.
The HTTP response was:
HTTP/1.1 500 InternalServerError
Set-Cookie: XSRF-TOKEN=aaaaaaaaaaaaaaaaaaaaaaa; path=/
Content-Type: application/json
Content-Length: 3963
{
"traceId": "traceba9919c5493c503a",
"message": "An error has occurred.",
"exceptionMessage": "Object reference not set to an instance of an object.",
"exceptionType": "NullReferenceException",
"stackTrace": " at SomeClass.SomeMethod() in C:\\dev\\Tracker.cs:line 111\r\n at SomeMethod() in C:\\dev\\workspace\\Someclasss.cs:line 43"
}
The originated HTTP request was:
The stacktrace property is not easily readable as it is formatted as a one-line string. Would be useful to intercept the response and replace "\r\n" with the actual new lines.
So an expected output would be
Expected HTTP response to be "OK", but found InternalServerError.
The HTTP response was:
HTTP/1.1 500 InternalServerError
Set-Cookie: XSRF-TOKEN=aaaaaaaaaaaaaaaaaaaaaaa; path=/
Content-Type: application/json
Content-Length: 3963
{
"traceId": "traceba9919c5493c503a",
"message": "An error has occurred.",
"exceptionMessage": "Object reference not set to an instance of an object.",
"exceptionType": "NullReferenceException",
"stackTrace": " at SomeClass.SomeMethod() in C:\\dev\\Tracker.cs:line 111
at SomeMethod() in C:\\dev\\workspace\\Someclasss.cs:line 43"
}
The originated HTTP request was:
And thus would ease reading the stack trace
Could you please update the references to 3rd party packages? Currently those old package versions are either vulnerable/deprecated themselves or bring in some other transient dependencies which are vulnerable/deprecated.
System.Text.Json
5.0.2 is deprecatedMicrosoft.AspNet.WebApi.Client
5.2.4 -> Newtonsoft.Json.Bson
1.0.1 -> NETStandard.Library
1.6.1 -> multiple vulnerable packagesOutput for a new xUnit test project with FluentAssertions.Web:
> dotnet list package --include-transitive --vulnerable
Transitive Package Resolved Severity Advisory URL
> System.Net.Http 4.3.0 High https://github.com/advisories/GHSA-7jgj-8wvc-jh57
> System.Text.RegularExpressions 4.3.0 High https://github.com/advisories/GHSA-cmhx-cq75-c4mj
> dotnet list package --include-transitive --deprecated
Transitive Package Resolved Reason(s) Alternative
> System.Text.Json 5.0.2 Other,Legacy
Actual
The HTTP response was:
HTTP/1.1 200 OK
Request-Context: appId=
Content-Type: application/json; charset=utf-8
Content-Length: 527079
***** Content is too large to display and only a part is printed. *****
{"consumption":[{"datetime":"2022-06-21T13:00:00","consumption":43.66},{"datetime":"2022-06-21T14:00:00","consumpti
Expected
The HTTP response was:
HTTP/1.1 200 OK
Request-Context: appId=
Content-Type: application/json; charset=utf-8
Content-Length: 1135
{
"consumption": [
{
"datetime": "2022-06-21T13:00:00",
"consumption": 43.66
},
I am always wary of updating packages if I don't know what's changed. I suggest adding a changelog to this repo, that contains at least the changes in 1.2.5.
For a suggested changelog format, see Keep a changelog. The most important part, however, is simply having a list of changes.
Given a BadRequest response, might be more readable to Assert error messages with more affinity
So an alternative API to
[Fact]
public async Task Post_WithNoAuthorAndNoContent_ReturnsBadRequest()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.PostAsync("/api/comments", new StringContent(@"{
""author"": """",
""content"": """"
}", Encoding.UTF8, "application/json"));
// Assert
response.Should().Be400BadRequest()
.And.HaveError("Author", "The Author field is required.")
.And.HaveError("Content", "The Content field is required.");
}
Would be
[Fact]
public async Task Post_WithNoAuthorAndNoContent_ReturnsBadRequest()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.PostAsync("/api/comments", new StringContent(@"{
""author"": """",
""content"": """"
}", Encoding.UTF8, "application/json"));
// Assert
response.Should().Be400BadRequest()
.And.HaveError("Author").And.HaveErrorMessage( "The Author field is required.")
.And.HaveError("Content").And.HaveErrorMessage("The Content field is required.");
}
or
[Fact]
public async Task Post_WithNoAuthorAndNoContent_ReturnsBadRequest()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.PostAsync("/api/comments", new StringContent(@"{
""author"": """",
""content"": """"
}", Encoding.UTF8, "application/json"));
// Assert
response.Should().Be400BadRequest()
.And.HaveError("Author").Which.ErrorMessage.Should().Be( "The Author field is required.")
.And.HaveError("Content").Which.ErrorMessage.Should().Be("The Content field is required.");
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.