Adding to Should().BeEquivalentTo() options.WithTracing() changes outcome of the test
See original GitHub issueDescription
I got an error message which I think should not have happened, since the only difference is the order of the item and by default order shouldn’t break equivalence. So I tried adding options.WithTracing()
to understand what’s going on, but then the test passes. This is the output without WithTracing()
:
Message: Expected item[0] to be
Deal.DealComputer+Deal
{
Items = {Deal.DealComputer+SingleItem
{
Name = "Croissant"
Price = 12
Tags = {Pastry}
}, Deal.DealComputer+SingleItem
{
Name = "Cappuccino"
Price = 11
Tags = {HotDrink}
}}
Name = "Coffee + Pastry"
Price = 19
Tags = {HotDrink, Pastry}
}, but found
Deal.DealComputer+Deal
{
Items = {Deal.DealComputer+SingleItem
{
Name = "Cappuccino"
Price = 11
Tags = {HotDrink}
}, Deal.DealComputer+SingleItem
{
Name = "Croissant"
Price = 12
Tags = {Pastry}
}}
Name = "Coffee + Pastry"
Price = 19
Tags = {HotDrink, Pastry}
}.
With configuration:
- Use declared types and members
- Compare enums by value
- Include all non-private properties
- Include all non-private fields
- Match member by name (or throw)
- Without automatic conversion.
- Without automatic conversion.
- Be strict about the order of items in byte arrays
Complete minimal example reproducing the issue
public enum Tag
{
HotDrink,
Pastry,
Breakfast
}
public interface Dish
{
string Name { get; set; }
int Price { get; set; }
IEnumerable<Tag> Tags { get; set; }
}
public class SingleItem : Dish, IEquatable<SingleItem>
{
public string Name { get; set; }
public int Price { get; set; }
public IEnumerable<Tag> Tags { get; set; }
public override bool Equals(object obj)
=> Equals(obj as SingleItem);
public bool Equals(SingleItem other)
=> !(other is null) &&
Name == other.Name &&
Price == other.Price &&
Tags.SequenceEqual(other.Tags);
public override int GetHashCode()
=> HashCode.Combine(Name, Price, Tags);
public static bool operator ==(SingleItem item1, SingleItem item2)
{
if (item1 is null)
{
return item2 is null;
}
return item1.Equals(item2);
}
public static bool operator !=(SingleItem item1, SingleItem item2)
=> !(item1 == item2);
}
public class Deal : Dish, IEquatable<Deal>
{
string Name { get; set; }
int Price { get; set; }
IEnumerable<Tag> Tags { get; set; }
}
public class SingleItem : Dish, IEquatable<SingleItem>
{
public string Name { get; set; }
public int Price { get; set; }
public IEnumerable<Tag> Tags { get; set; }
public override bool Equals(object obj)
=> Equals(obj as SingleItem);
public bool Equals(SingleItem other)
=> !(other is null) &&
Name == other.Name &&
Price == other.Price &&
Tags.SequenceEqual(other.Tags);
public override int GetHashCode()
=> HashCode.Combine(Name, Price, Tags);
public static bool operator ==(SingleItem item1, SingleItem item2)
{
if (item1 is null)
{
return item2 is null;
}
return item1.Equals(item2);
}
public static bool operator !=(SingleItem item1, SingleItem item2)
=> !(item1 == item2);
}
public class Deal : Dish, IEquatable<Deal>
{
public string Name { get; set; }
public int Price { get; set; }
public IEnumerable<Tag> Tags { get; set; }
public IEnumerable<SingleItem> Items { get; set; }
public override bool Equals(object obj) => Equals(obj as Deal);
public bool Equals(Deal other)
{
return !(other is null) &&
Name == other.Name &&
Price == other.Price &&
Tags.SequenceEqual(other.Tags) &&
Items.SequenceEqual(other.Items);
}
public override int GetHashCode() => HashCode.Combine(Name, Price, Tags, Items);
public static bool operator ==(Deal deal1, Deal deal2)
{
if (deal1 is null)
{
return deal2 is null;
}
return deal1.Equals(deal2);
}
public static bool operator !=(Deal deal1, Deal deal2) => !(deal1 == deal2);
}
readonly SingleItem cappuccino = new SingleItem { Name = "Cappuccino", Price = 11, Tags = new[] { Tag.HotDrink }};
readonly SingleItem croissant = new SingleItem { Name = "Croissant", Price = 12, Tags = new[] { Tag.Pastry } };
readonly SingleItem tea = new SingleItem { Name = "Tea", Price = 8, Tags = new[] { Tag.HotDrink } };
readonly SingleItem datesPastry = new SingleItem { Name = "Dates Pastry", Price = 10, Tags = new[] { Tag.Pastry } };
readonly Deal CoffeeAndPastryDeal = new Deal { Name = "Coffee + Pastry", Price = 19, Tags = new [] { Tag.HotDrink, Tag.Pastry } };
[Fact]
public void FourItemsSingleDeal()
{
var items = new[] {
tea,
datesPastry,
cappuccino,
croissant,
};
var deals = new[] { CoffeeAndPastryDeal };
var completedDeal1 = CoffeeAndPastryDeal.DeepClone();
completedDeal1.Items = new[] { croissant, cappuccino };
var expected = new Dish[]
{
completedDeal1,
tea,
datesPastry
};
var completedDeal2 = CoffeeAndPastryDeal.DeepClone();
completedDeal2.Items = new[] { cappuccino, croissant };
var actual =
new Dish[]
{
completedDeal2,
tea,
datesPastry
};
actual.Should().BeEquivalentTo(expected, o => o.WithTracing()); // passes
actual.Should().BeEquivalentTo(); //fails
}
Expected behavior:
I expect options.WithTracing()
to not affect the outcome of tests
Actual behavior:
options.WithTracing()
affects the outcome of tests
Versions
- Which version of Fluent Assertions are you using? 5.4.1
- Which .NET runtime and version are you targeting? .net core 2.1
- For deep cloning I’m using nuget DeepCloner 0.10.2
Issue Analytics
- State:
- Created 5 years ago
- Comments:12 (7 by maintainers)
Top Results From Across the Web
FluentAssertions Should().BeEquivalentTo() fails in trivial ...
It seems it tries to treat the Address object as a string (because it overrides ToString() ?). I tried to use the options.ComparingByMembers< ......
Read more >Object graph comparison
By default, Should(). BeEquivalentTo() compares Enum members by the enum's underlying numeric value. An option to compare an Enum only by name is...
Read more >Fluent Assertions 5.0: The best unit test assertion library in the ...
You'll now find that all assertions start with Should() , e.g.. object. ... The changes to BeEquivalentTo will be the most visible ones:....
Read more >Just Use FluentAssertions - Tinkerer
Option 1: Implementing Object.Equals for use in tests Now, I'm not a huge fan of implementing Object. Equals() in general, and I've written...
Read more >Improving Unit Tests with Fluent Assertions
In this article, we are going to learn how we can improve our unit tests using Fluent Assertions library with .NET project.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Thanks for the detailed report! Excellent investigation
Wow. Good catch @jnyrup