Skip to content

Several issues with IValueFormatter #1527

@rcdailey

Description

@rcdailey

Description

I have a number of issues with the value formatter system right now. To summarize:

  • Lack of documentation. I've found nothing that explains how to add a value formatter to Fluent Assertions. Only thing I found was a very brief section in the docs with a small example. It doesn't tell me much.
    • Also there is no information on how to properly use FormatChild.
  • I have a custom object that keep as a value in a dictionary. I never see CanHandle() invoked for this type.

Complete minimal example reproducing the issue

Concerning the custom IValueFormatter, I have mine set up like this right now:

public class ProfileDataValueFormatter : IValueFormatter
{
    public bool CanHandle(object value)
    {
        return value is ProfileData;
    }

    public string Format(object value, FormattingContext context, FormatChild formatChild)
    {
        var profileData = (ProfileData) value;
        return $"{profileData.Ignored}";
    }
}

The Format() method right now is completely wrong; my goal is just to see something happen, I was planning on flushing it out once I understand more about how custom value formatters work.

ProfileData is defined as follows:

public class ProfileData
{
    public List<string> Required { get; init; } = new();
    public List<string> Ignored { get; init; } = new();
    public Dictionary<int, List<string>> Preferred { get; init; } = new();
}

The test case invoking the failed assertion is set up like so (there's a bit of missing context to understand what is going on, but this is the gist of it):

var results = context.ParseWithDefaults(markdown);

var expectedResults = new Dictionary<string, ProfileData>
{
    {
        "Test Release Profile", new ProfileData
        {
            Ignored = new List<string> {"added1"},
            Preferred = new Dictionary<int, List<string>>
            {
                {10, new List<string> {"added2", "added3"}}
            }
        }
    },
    { "Second Release Profile", new ProfileData()}
};

results.Should().Equal(expectedResults);

The last line where I do Should().Equal() is failing. It prints like so:

Expected results to be equal to {[Test Release Profile, Trash.Sonarr.ReleaseProfile.ProfileData], [Second Release Profile, Trash.Sonarr.ReleaseProfile...

Expected behavior:

The whole reason I want a custom formatter is because I want something meaningful to print out for Trash.Sonarr.ReleaseProfile.ProfileData, which is pretty useless right now for debugging purposes. What would be ideal is to have the members of ProfileData printed in a JSON-like format. I want the values in all of the containers of that object to use Fluent's built-in rendering.

I lack the documentation to achieve this goal, but the reason I've filed the bug is not just because of documentation. The main reason is that I believe CanHandle() should receive an object that can be cast to ProfileData, but when I step through the debugger the furthest down it seems to go is KeyValuePair<string, ProfileData>. I was expecting one for string (which I think I did see) and another call for ProfileData as it "walks down" the object tree.

Actual behavior:

Sort of explained that above...

Versions

  • Which version of Fluent Assertions are you using? 5.*
  • Which .NET runtime and version are you targeting? E.g. .NET framework 4.6.1 or .NET Core 2.1. .NET 5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions