Code Monkey home page Code Monkey logo

Comments (6)

dennisdoomen avatar dennisdoomen commented on June 3, 2024 1

Looking at the example a bit closer, I notice that you also override Equals. That is the reason why FA assumes you want it to treat the type as having value semantics. It doesn't do anything specifically with IEquatable

from fluentassertions.

jnyrup avatar jnyrup commented on June 3, 2024 1

First, thanks for the detailed and kind description ❤️

I see how Excluding() being ignored when FA choose to compare T using value semantics is not intuitive.

It seems to me the question is whether Excluding<T>(e => e.ETag) should also imply ComparingByMembers<T>()?
Or at least make some noise instead of silently being ignored.

The internal EqualityStrategyProvider.GetEqualityStrategy can be used to check how FA is currently going to compare Ts.
I emphasize currently because Excluding<T>(e => e.ETag).ComparingByMembers<T>() is also valid syntax.

I tried as a quick hack to change EquivalencyOptions<TExpectation>.Excluding to

public EquivalencyOptions<TExpectation> Excluding(Expression<Func<TExpectation, object>> expression)
{
    var strategy = EqualityStrategyProvider.GetEqualityStrategy(typeof(TExpectation));
    if (strategy is EqualityStrategy.Equals)
    {
        ComparingByMembers<TExpectation>();
    }

    AddSelectionRule(new ExcludeMemberByPathSelectionRule(expression.GetMemberPath()));
    return this;
}

and the provided test passed along will all existing tests 🤷

A problem with this hack is that it doesn't work for the non-generic SelfReferenceEquivalencyOptions.Excluding(Expression<Func<IMemberInfo, bool>> predicate) which leads to inconsistent behavior depending on which overload of Excluding() is used.
This could probably be workarounded by moving/copying the non-generic variant to EquivalencyOptions<TExpectation>, but that's a later discussion.

from fluentassertions.

divega avatar divega commented on June 3, 2024 1

Instead, we can fail the assertion when you try to use Including and Excluding on a type that is being treated as having value semantics.

I actually agree that this is the most logic consequence given that you have decided that those types have value semantics. Any attempt to customize how comparison work for types that have value semantics should fail.

I expect that a small number of Fluent Assertions users could have exclusion calls that they didn't need and will experience the new behavior as a breaking change. But for the majority of users who did need the exclusions to work, there is an opportunity to throw an informative exception.

And I actually think that having to call ComparingByMembers<object>() or similar is a decent workaround if you need to do generic exclusions. It just isn't working great for me as reflected in #1757 (comment) 😞

from fluentassertions.

divega avatar divega commented on June 3, 2024

Thanks a lot both @dennisdoomen and @jnyrup for looking into this. Interesting point that it’s about Equals(object) and not IEquatable<T>. I didn’t notice because all my classes under test have both, but also I didn’t think FA could differentiate between object.Equals and an override.

BTW, I remember trying using record instead of class while putting together the repro, and I was surprised that the behavior was different. IIRC, it did member-wise comparison for records. My understanding is that records also override Equals and implement IEquatable<T>, albeit automatically, so I am curious why and how FA treats them differently. I hope I am remembering what I saw correctly. Away from my computer at the moment.

from fluentassertions.

divega avatar divega commented on June 3, 2024

Today I found something that I believe aggravates the issue:

I have in my test code some assertion helper methods that do "soft" property matching like this:

opt = opt.Excluding(o => o.Path.Contains(ignoredField));

This code is supposed to exclude any top-level properties or nested properties that match the condition.

I can easily add a ComparingByMembers<T>() call for the top-level type because the specific T is known and available to the compiler from the generic arguments of the helper method, but for any of the nested properties, this doesn't work.

Currently investigating if there is a workaround I can apply for this that doesn't require me to list all the nested types my top-level types can contain.

from fluentassertions.

dennisdoomen avatar dennisdoomen commented on June 3, 2024

It seems to me the question is whether Excluding<T>(e => e.ETag) should also imply ComparingByMembers<T>()?
Or at least make some noise instead of silently being ignored.

I don't think that is what we should be doing. Instead, we can fail the assertion when you try to use Including and Excluding on a type that is being treated as having value semantics. The only caveat is that we can only do that if the exclusion/inclusion can be directly associated with a particular path. A generic exclusion such as the one mention by @divega, cannot do that, since it may apply on any level.

from fluentassertions.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.