Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FluentAssertions - Actual body is not displayed in error message when using Json Body #1084

Closed
srollinet opened this issue Mar 18, 2024 · 6 comments · Fixed by #1085
Closed
Labels

Comments

@srollinet
Copy link

srollinet commented Mar 18, 2024

Describe the bug

When using a Json Body, Fluent assertion cannot format the actual body in the error message

Expected wiremockserver to have been called using body { param = value2 }, but didn't find it among the body {{{{empty}}}}.

Expected behavior:

The json body is correctly formatted

Expected wiremockserver to have been called using body { param = value2 }, but didn't find it among the body {{
"param": "value"
}}.

Test to reproduce

You can reproduce the message with this unit test (proper assertion is not done, the test fails with the unformatted message)

    [Fact]
    public async Task HaveReceived1Call_WithBodyAsJson_UsingJsonMatcher()
    {
        // Arrange
        var server = WireMockServer.Start();

        server
            .Given(Request.Create().WithPath("/a").UsingPost())
            .RespondWith(Response.Create().WithBody("A response"));

        // Act
        var httpClient = new HttpClient();

        var body = new
        {
            param = "value"
        };
        await httpClient.PostAsJsonAsync($"{server.Url}/a", body);

        body = new
        {
            param = "value2"
        };
        // Assert
        server
            .Should()
            .HaveReceived(1)
            .Calls()
            .WithBodyAsJson(new JsonMatcher(body))
            .And
            .UsingPost();

        server.Stop();
    }

Other related info

One solution is to implement a custom IValueFormatter

Example:

public class JObjectFormatter : IValueFormatter
{
    public bool CanHandle(object value)
    {
        return value is JObject;
    }

    public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild)
    {
        var jObject = (JObject)value;
        formattedGraph.AddFragment(jObject.ToString());
    }
}

And to add it in the custom formatters Formatter.AddFormatter(new JObjectFormatter());

Note: the IValueFormatter interface varies based on the target framework, so it must be multiple implementations

Let me know if I can help or if I can make a PR for this issue

@srollinet srollinet added the bug label Mar 18, 2024
@StefH
Copy link
Collaborator

StefH commented Mar 18, 2024

@srollinet
Could it also be an option to just create a helper method in WireMockAssertions.WithBody.cs which formats all possible body values?

@srollinet
Copy link
Author

It could also probably be an option. I can check that next day if you want.

@StefH
Copy link
Collaborator

StefH commented Mar 18, 2024

Sure. A PR is welcome.

@srollinet
Copy link
Author

Okay, doing something like this makes the message better

    public AndConstraint<WireMockAssertions> WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
    {
        var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);

        return ExecuteAssertionWithBodyAsIObjectMatcher(matcher, because, becauseArgs, condition, filter, r => FormatBody(r.BodyAsJson));
    }

    private static object? FormatBody(object? body)
    {
        if (body is JToken jToken)
        {
            return jToken.ToString(Formatting.None);
        }
        
        //TODO Add other types here

        return body;
    }

Expected wiremockserver to have been called using body { param = value2, otherParam = 18067588-16df-4f27-ab2b-a737206f97ff }, but didn't find it among the body {"{"param":"value","otherParam":42}"}.

But it is still different between the expected body and the actual body

And it is worse if the Matcher is created by using a named type, or even worse if the type overrides ToString()

Expected wiremockserver to have been called using body WireMock.Net.Tests.FluentAssertions.WireMockAssertionsTests+RequestInputDto
{
OtherParam = {718de8cd-21ef-4406-b631-1cd99c1a4328},
Param = "value"
}, but didn't find it among the body {"{"Param":"value","OtherParam":"6d3380ff-bfcf-4269-8d81-01ac24d13645"}"}.

I wonder if the expected body should also be serialized as json, so both representations will be similar. What do you think?

@StefH
Copy link
Collaborator

StefH commented Mar 19, 2024

@srollinet
Can you take a look at my PR ?

@srollinet
Copy link
Author

@StefH Yes, it looks good to me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants