Skip to content

Commit

Permalink
Provide opt-out to AssertionOptions(o => o.WithStrictOrdering()) (#974
Browse files Browse the repository at this point in the history
)

* added WithoutStrictOrdering to SelfReferenceEquivalencyAssertionOptions

* reverted unintended change

* adapted tests to expected WithoutStrictOrdering to reset ByteArrayOrderingRule

* WithoutStrictOrdering doesn't reset strict ordering for bytes. Introducing WithoutStrictOrderingForBytes to ignore ordering of bytes.

* removed SelfReferenceEquivalenyAssertionOptions.WithoutStrictOrderingForBytes() and made OrderingRuleCollection.Clear() internal.

* updating documentation to mention `WithoutStrictOrdering` and `WithoutStrictorderingFor(...)`.

* removed obsolete reference to WithoutStrictOrderingForBytes
  • Loading branch information
BrunoJuchli authored and jnyrup committed Dec 5, 2018
1 parent b442c65 commit 31d55a9
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 1 deletion.
5 changes: 5 additions & 0 deletions Src/FluentAssertions/Equivalency/OrderingRuleCollection.cs
Expand Up @@ -56,6 +56,11 @@ public void Add(IOrderingRule rule)
rules.Add(rule);
}

internal void Clear()
{
rules.Clear();
}

/// <summary>
/// Determines whether the rules in this collection dictate strict ordering during the equivalency assertion on
/// the collection pointed to by <paramref name="memberInfo"/>.
Expand Down
Expand Up @@ -435,6 +435,7 @@ public TSelf Using(IEquivalencyStep equivalencyStep)
/// </summary>
public TSelf WithStrictOrdering()
{
orderingRules.Clear();
orderingRules.Add(new MatchAllOrderingRule());
return (TSelf)this;
}
Expand All @@ -449,6 +450,16 @@ public TSelf WithStrictOrderingFor(Expression<Func<IMemberInfo, bool>> predicate
return (TSelf)this;
}

/// <summary>
/// Causes all collections - except bytes - to be compared ignoring the order in which the items appear in the expectation.
/// </summary>
public TSelf WithoutStrictOrdering()
{
orderingRules.Clear();
orderingRules.Add(new ByteArrayOrderingRule());
return (TSelf)this;
}

/// <summary>
/// Causes the collection identified by the provided <paramref name="predicate" /> to be compared ignoring the order
/// in which the items appear in the expectation.
Expand Down
306 changes: 306 additions & 0 deletions Tests/Shared.Specs/CollectionEquivalencySpecs.cs
Expand Up @@ -302,6 +302,56 @@ public void When_a_byte_array_does_not_match_strictly_it_should_throw()
.WithMessage("Expected*item[0]*6*1*");
}

[Fact]
public void When_a_byte_array_does_not_match_strictly_and_order_is_not_strict_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new byte[] { 1, 2, 3, 4, 5, 6 };

var expectation = new byte[] { 6, 5, 4, 3, 2, 1 };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().BeEquivalentTo(expectation, options => options.WithoutStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>()
.WithMessage("Expected*item[0]*6*1*");
}

[Fact]
public void When_a_collection_property_is_a_byte_array_which_does_not_match_strictly_and_order_is_not_strict_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new
{
bytes = new byte[] { 1, 2, 3, 4, 5, 6 }
};

var expectation = new
{
bytes = new byte[] { 6, 5, 4, 3, 2, 1 }
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().BeEquivalentTo(expectation, options => options.WithoutStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>()
.WithMessage("Expected *member bytes[0]*6*1*");
}

[Fact]
public void When_a_collection_does_not_match_it_should_include_items_in_message()
{
Expand Down Expand Up @@ -1094,6 +1144,54 @@ public void When_an_unordered_collection_must_be_strict_using_a_predicate_it_sho
.WithMessage("*Expected item[0].UnorderedCollection*5 item(s)*empty collection*");
}

[Fact]
public void When_an_unordered_collection_must_be_strict_using_a_predicate_and_order_was_reset_to_not_strict_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 1, 2, 3, 4, 5 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

var expectation = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 5, 4, 3, 2, 1 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().BeEquivalentTo(expectation, options =>
options
.WithStrictOrderingFor(s => s.SelectedMemberPath.Contains("UnorderedCollection"))
.WithoutStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().NotThrow();
}

[Fact]
public void When_an_unordered_collection_must_be_strict_using_an_expression_it_should_throw()
{
Expand Down Expand Up @@ -1146,6 +1244,56 @@ public void When_an_unordered_collection_must_be_strict_using_an_expression_it_s
"*Expected item[0].UnorderedCollection*5 item(s)*empty collection*");
}

[Fact]
public void When_an_unordered_collection_must_be_strict_using_an_expression_and_order_is_reset_to_not_strict_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 1, 2, 3, 4, 5 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

var expectation = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 5, 4, 3, 2, 1 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action =
() =>
subject.Should().BeEquivalentTo(expectation,
options => options
.WithStrictOrderingFor(s => s.UnorderedCollection)
.WithoutStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().NotThrow();
}

[Fact]
public void When_an_unordered_collection_must_not_be_strict_using_a_predicate_it_should_not_throw()
{
Expand Down Expand Up @@ -1193,6 +1341,56 @@ public void When_an_unordered_collection_must_not_be_strict_using_a_predicate_it
action.Should().NotThrow();
}

[Fact]
public void When_an_unordered_collection_must_not_be_strict_using_a_predicate_and_order_was_reset_to_strict_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 1, 2 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

var expectation = new[]
{
new
{
Name = "John",
UnorderedCollection = new[] { 2, 1 }
},
new
{
Name = "Jane",
UnorderedCollection = new int[0]
}
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().BeEquivalentTo(expectation, options => options
.WithStrictOrdering()
.WithoutStrictOrderingFor(s => s.SelectedMemberPath.Contains("UnorderedCollection"))
.WithStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>()
.WithMessage(
"*Expected item[0].UnorderedCollection[0] to be 2, but found 1.*Expected item[0].UnorderedCollection[1] to be 1, but found 2*");
}

[Fact]
public void
When_asserting_equivalence_of_collections_and_configured_to_use_runtime_properties_it_should_respect_the_runtime_type
Expand Down Expand Up @@ -2584,6 +2782,114 @@ public void When_two_unordered_lists_are_structurally_equivalent_and_order_is_st
"Expected item[0].Name*Jane*John*item[1].Name*John*Jane*");
}

[Fact]
public void When_two_unordered_lists_are_structurally_equivalent_and_order_was_reset_to_strict_it_should_fail()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new[]
{
new Customer
{
Name = "John",
Age = 27,
Id = 1
},
new Customer
{
Name = "Jane",
Age = 24,
Id = 2
}
};

var expectation = new Collection<Customer>
{
new Customer
{
Name = "Jane",
Age = 24,
Id = 2
},
new Customer
{
Name = "John",
Age = 27,
Id = 1
}
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().BeEquivalentTo(
expectation,
options => options
.WithStrictOrdering()
.WithoutStrictOrdering()
.WithStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>()
.WithMessage(
"Expected item[0].Name*Jane*John*item[1].Name*John*Jane*");
}

[Fact]
public void
When_two_unordered_lists_are_structurally_equivalent_and_order_was_reset_to_not_strict_it_should_succeed
()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new[]
{
new Customer
{
Name = "John",
Age = 27,
Id = 1
},
new Customer
{
Name = "Jane",
Age = 24,
Id = 2
}
};

var expectation = new Collection<Customer>
{
new Customer
{
Name = "Jane",
Age = 24,
Id = 2
},
new Customer
{
Name = "John",
Age = 27,
Id = 1
}
};

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action =
() => subject.Should().BeEquivalentTo(expectation, x => x.WithStrictOrdering().WithoutStrictOrdering());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
action.Should().NotThrow();
}

[Fact]
public void
When_two_unordered_lists_are_structurally_equivalent_it_should_succeed
Expand Down
15 changes: 14 additions & 1 deletion docs/_pages/documentation.md
Expand Up @@ -926,7 +926,20 @@ You can even tell FA to use strict ordering only for a particular collection or
orderDto.Should().BeEquivalentTo(expectation, options => options.WithStrictOrderingFor(s => s.Products));
```

**Notice:** For performance reasons, collections of bytes are compared in exact order.
And you can tell FA to generally use strict ordering but ignore it for a particular collection or dictionary member:

```csharp
orderDto.Should().BeEquivalentTo(expectation, options => options.WithStrictOrdering().WithoutStrictOrderingFor(s => s.Products));
```

In case you chose to use strict ordering by default you can still configure non-strict ordering in specific tests:
```csharp
AssertionOptions.AssertEquivalencyUsing(options => options.WithStrictOrdering());

orderDto.Should().BeEquivalentTo(expectation, options => options.WithoutStrictOrdering());
```

**Notice:** For performance reasons, collections of bytes are compared in exact order. This is even true when applying `WithoutStrictOrdering()`.

### Diagnostics
`Should().BeEquivalentTo` is a very powerful feature, and one of the unique selling points of Fluent Assertions. But sometimes it can be a bit overwhelming, especially if some assertion fails under unexpected conditions. To help you understand how Fluent Assertions compared two (collections of) object graphs, the failure message will always include the relevant configuration settings:
Expand Down

0 comments on commit 31d55a9

Please sign in to comment.