fluffynuts / NExpect

An assertions framework for .NET with a BDD-like feel, inspired by Chai and Jasmine, designed to be user-extensible
BSD 3-Clause "New" or "Revised" License
22 stars 8 forks source link

UnmetExpectationException does not use ToString for Equal matcher #30

Closed UrsMetz closed 2 years ago

UrsMetz commented 2 years ago

In our code base have quite a few classes and structs that have no public properties. When using expectations like in the code sample below the failure messages aren't very descriptive: This test

using NExpect;
using Xunit;
using static NExpect.Expectations;

namespace NExpectExamples;

public class FailureMessagesTest
{
    [Fact]
    public void FailureMessages()
    {
        var a = new MyStruct("a");
        var b = new MyStruct("b");

        Expect(a).To.Equal(b);
    }
}

public struct MyStruct
{
    private readonly string _text;

    public MyStruct(string text)
    {
        _text = text;
    }

    public override string ToString()
    {
        return $"{nameof(_text)}: {_text}";
    }
}

yields the following failure message:

NExpect.Exceptions.UnmetExpectationException: Expected {} but got {}

One instance where this occurs is using Option from https://github.com/la-yumba/functional-csharp-code-2.

I understand that the JSON-like stringification is useful for objects with public properties and no overridden ToString method but in our case it would be nice when NExpect either

fluffynuts commented 2 years ago

I will look into that

Every matcher should allow passing in a Func<string> which can be used to generate a more descriptive error. Have you tried (in the mean time):

Expect(a)
   .To.Equal(b, () => $"Expected {b.ToString()}, but found {a.ToString()}");
UrsMetz commented 2 years ago

Thanks for the swift response. Unfortunately .To.Deep.Equal gives false positives: as far as I understand it does not consider private fields but only public properties (and maybe fields?).

fluffynuts commented 2 years ago

Thanks for the swift response. Unfortunately .To.Deep.Equal gives false positives: as far as I understand it does not consider private fields but only public properties (and maybe fields?).

Correct, which is why I edited (sorry, you must have been responding in the mean time)

UrsMetz commented 2 years ago

Ah, you were too fast :D The Func<string> thing would work but is a bit annoying when you have a lot of expectations for classes and structs with only private state.

fluffynuts commented 2 years ago

Ah, you were too fast :D The Func<string> thing would work but is a bit annoying when you have a lot of expectations for classes and structs with only private state.

Yeah, I get that; I'm thinking that perhaps if Stringify essentially comes up with an empty object ({}) then perhaps I could have a fallback to look for the first .ToString() in the inheritance hierarchy which does not come from object. Thoughts?

UrsMetz commented 2 years ago

Stringify falling back to ToString when it produces {} sounds like a plan :-). At least that would work in our case.

fluffynuts commented 2 years ago

I'll try to have a look at this tomorrow (: it's 21h30 here now (:

fluffynuts commented 2 years ago

@UrsMetz please try 1.0.274

UrsMetz commented 2 years ago

Looks good, closing. Thanks for the fast fix :-).