question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add an option to BeEquivalentTo that compares types of objects recursively.

See original GitHub issue

I am trying to compare AST and found that BeEquivalentTo has no option to instruct the library to check types on comparing. For example, I expect the way to modify the following test code to fail because of class names do not match. This behavior should be recursive (so if property value does not have exactly the same type, comparison should fail). I see that stackoverflow has a question on this.

class OneThing
{
	public string Name => "Test";
}
class OtherThing
{
	public string Name => "Test";
}

[TestMethod]
public void MyTestMethod()
{
	new OneThing().Should().BeEquivalentTo(new OtherThing(), o => o.RespectingRuntimeTypes());
}

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:5
  • Comments:21 (11 by maintainers)

github_iconTop GitHub Comments

2reactions
BrunoJuchlicommented, Nov 12, 2020

Urs Enzler has an alternative IEquivalencyStep implementation which is a bit more fine grained, see: https://github.com/fluentassertions/fluentassertions/issues/978#issuecomment-441204425


I quite unexpectedly fell into this trap as well - had tests passing which should be failing. We’ve been using FluentAssertions since v2 or ever v1 and I never really picked up on the importance of this v5 change until I stumbled onto it today by accidence. Tragically, the tests which should have failed have been written 2 years ago and no-one noticed the error until today (this was only possible because the “expected” value is wrong, not the “actual” – otherwise customers would have complained long ago 😉 ).

I always understood the BeEquivalentTo as a generic way to compare equality without having to implement (and test!) equality on the object manually. Another benefit is the more verbose assertion message which tells you the specific difference, instead of a more generic “it’s not the same” (go figure out for yourself how they differ via debugging). So for me were very good reasons for using BeEquivalentTo outside of comparing a source object to it’s mapped variant (which, by incident, we don’t even care too much about in the case of mapped objects because we’re mostly using AutoMapper for that which itself has some functionality to detect if you forget about mapping properties…).

Therefore I’d very much like FluentAssertions to have an equivalency verification in a more stricter sense.


Nitpicking: I don’t think having the same property values necessarily constitutes equivalency (in some cases it will). Let me give an example:

Salary { Dollars = 5000 } vs BossSalary { Dollars 5000, Gold = 5 tons }

Pretty sure you wouldn’t consider these salaries as equivalent. But might actually consider these two as equivalent:

DollarSalary { Dollars = 5000 } vs EuroSalary { Euros = 4250 } (at the time of writing 5000 USD are worth 4250 EUR).

As such the difference between “BeEqual” and “BeEquivalentTo” is not immediately obvious. It seems much simpler to define equality than equivalency. I’d even go so far as to say one would expect equivalency needing to be tailored to a specific use case.

On a conceptual level I’d prefer assertions to fail more often, and then having to opt in to being less strict. That would save me from making wrong assumptions.


Let me end by saying thank you for the great FluentAssertions library you’ve provided and maintained for many years now. Also, I’ve learned a lot from your work. Really appreciate it.

2reactions
arnileibovitscommented, Jan 18, 2021

I solved this by using a custom IEquivalencyStep:

[TestMethod]
public void MyTestMethod()
{
    a.Should().BeEquivalentTo(b, options => options
        .RespectingRuntimeTypes()
        .Using(new TypeEqualityEquivalencyStep()));
}

public class TypeEqualityEquivalencyStep : IEquivalencyStep
{
    public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
    {
        return true; // Always handle
    }

    public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator
        structuralEqualityValidator, IEquivalencyAssertionOptions config)
    {
        var subjectType = context.Subject?.GetType();
        var expectationType = context.Expectation?.GetType();

        subjectType?.Should().Be(expectationType, context.Because, context.BecauseArgs);

        if (subjectType?.GetProperties().Any() != true)
        {
            return true; // Don't compare empty class
        }

        return false; // Continue to next step
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

FluentAssertions Should().BeEquivalentTo() fails with Lists ...
The test currently fails. I'm suspecting it has something to do with Should().BeEquivalentTo not being fit to compare run-time specified types.
Read more >
Object graph comparison
Imagine you want to compare an Order and an OrderDto using BeEquivalentTo , but the first type has a Name property and the...
Read more >
How to compare big objects with FluentAssertsions?
From the documentation, it compares with a recursion of 10 levels and it it's used like this: orderDto.Should().BeEquivalentTo(order);.
Read more >
C# Fluent Assertions for Unit Testing
The BeEquivalentTo method is your magic wand for comparing object graphs. It recursively compares all the properties of the objects, ...
Read more >
Fluent Assertions 6.0, the biggest release ever
Fluent Assertions' flagship feature has always been the ability to do a deep recursive comparison between two object graphs.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found