Skip to content

Commit

Permalink
Object Graph Comparison - ShouldBeEquivalentTo(...) (#411)
Browse files Browse the repository at this point in the history
* Define public API.

* Add type comparison logic, message generation, and unit test.

* Add ValueType comparison logic and unit tests.

* Add null value comparison logic and unit tests.

* Add referential comparison logic and unit test.

* Add string comparison logic and unit tests.

* Add IEnumerable comparison logic and unit tests.
This change involves change the datatype of the 'path' variable from IList<T> to IEnumerable<T> to get LINQ functionality (and maybe some better performance due to fewer passes through the lists).

* Add child property comparison logic and unit tests.
This introduces the actual recursion into the comparison logic.
Some refactoring of code is included to call attention to the places where the object graph path is modified.

* Add infinite loop detection logic and unit tests.
This introduces a dictionary that keeps track of which objects have previously been (or are currently being) compared. Any new child comparisons will first check this dictionary; if the comparison has already been done between the current objects, then they are simply skipped. This should avoid infinite loop problems.
The dictionary is keyed by the actual object being compared, and the values represent a list of objects that the key has been compared against.
  • Loading branch information
TaffarelJr authored and josephwoodward committed Jan 14, 2019
1 parent efc05f1 commit f44f1ef
Show file tree
Hide file tree
Showing 12 changed files with 913 additions and 3 deletions.
117 changes: 117 additions & 0 deletions src/Shouldly.Tests/ShouldBeEquivalentTo/EnumerableScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using Shouldly.Tests.Strings;
using Xunit;

namespace Shouldly.Tests.ShouldBeEquivalentTo
{
public class EnumerableScenario
{
[Fact]
public void ShouldFailWhenListIsTooShort()
{
var subject = new[] { 1, 2, 3, 4 };
Verify.ShouldFail(() =>
subject.ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5 }, "Some additional context"),

errorWithSource:
@"Comparing object equivalence, at path:
subject [System.Int32[]]
Count
Expected value to be
5
but was
4
Additional Info:
Some additional context",

errorWithoutSource:
@"Comparing object equivalence, at path:
<root> [System.Int32[]]
Count
Expected value to be
5
but was
4
Additional Info:
Some additional context");
}

[Fact]
public void ShouldFailWhenListIsTooLong()
{
var subject = new[] { 1, 2, 3, 4, 5, 6 };
Verify.ShouldFail(() =>
subject.ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5 }, "Some additional context"),

errorWithSource:
@"Comparing object equivalence, at path:
subject [System.Int32[]]
Count
Expected value to be
5
but was
6
Additional Info:
Some additional context",

errorWithoutSource:
@"Comparing object equivalence, at path:
<root> [System.Int32[]]
Count
Expected value to be
5
but was
6
Additional Info:
Some additional context");
}

[Fact]
public void ShouldFailWhenListsDoNotMatch()
{
var subject = new[] { 1, 2, 6, 4, 5 };
Verify.ShouldFail(() =>
subject.ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5 }, "Some additional context"),

errorWithSource:
@"Comparing object equivalence, at path:
subject [System.Int32[]]
Element [2] [System.Int32]
Expected value to be
3
but was
6
Additional Info:
Some additional context",

errorWithoutSource:
@"Comparing object equivalence, at path:
<root> [System.Int32[]]
Element [2] [System.Int32]
Expected value to be
3
but was
6
Additional Info:
Some additional context");
}

[Fact]
public void ShouldPass()
{
var subject = new[] { 1, 2, 6, 4, 5 };
subject.ShouldBeEquivalentTo(new[] { 1, 2, 6, 4, 5 });
}
}
}
14 changes: 14 additions & 0 deletions src/Shouldly.Tests/ShouldBeEquivalentTo/FakeObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Collections;
using System.Collections.Generic;

namespace Shouldly.Tests.ShouldBeEquivalentTo
{
public class FakeObject
{
public int Id { get; set; }
public string Name { get; set; }
public FakeObject Child { get; set; }
public ICollection<string> Colors { get; set; }
public IEnumerable Adjectives { get; set; }
}
}
79 changes: 79 additions & 0 deletions src/Shouldly.Tests/ShouldBeEquivalentTo/NullScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Shouldly.Tests.Strings;
using Xunit;

namespace Shouldly.Tests.ShouldBeEquivalentTo
{
public class NullScenario
{
[Fact]
public void ShouldFailWhenActualIsNull()
{
string subject = null;
Verify.ShouldFail(() =>
subject.ShouldBeEquivalentTo("Hello", "Some additional context"),

errorWithSource:
@"Comparing object equivalence, at path:
subject
Expected value to be
""Hello""
but was
null
Additional Info:
Some additional context",

errorWithoutSource:
@"Comparing object equivalence, at path:
<root>
Expected value to be
""Hello""
but was
null
Additional Info:
Some additional context");
}

[Fact]
public void ShouldFailWhenExpectedIsNull()
{
const string subject = "Hello";
Verify.ShouldFail(() =>
subject.ShouldBeEquivalentTo(null, "Some additional context"),

errorWithSource:
@"Comparing object equivalence, at path:
subject
Expected value to be
null
but was
""Hello""
Additional Info:
Some additional context",

errorWithoutSource:
@"Comparing object equivalence, at path:
<root>
Expected value to be
null
but was
""Hello""
Additional Info:
Some additional context");
}

[Fact]
public void ShouldPassWhenBothAreNull()
{
string subject = null;
subject.ShouldBeEquivalentTo(null);
}
}
}
Loading

0 comments on commit f44f1ef

Please sign in to comment.