jamesfoster / deepequal Goto Github PK
View Code? Open in Web Editor NEWAn extensible deep comparison for .NET
License: MIT License
An extensible deep comparison for .NET
License: MIT License
I notice that in the latest version, you are sticking Difference objects into the ComparisonContext. Tracking differences and having custom formatters could certainly make sense. But I think this is the wrong way to go about it. Logically, the Difference objects should be part of a Result object, not part of the ComparisonContext object. In other words, you would have a ResultModel object, or something like that. One of its fields could be a ComparisonResult -- pass, fail, or inconclusive. Another field might contain your differences.
In addition to making logical sense, this would eliminate the need to return two objects from your Compare methods.
The library has several extension methods on object (WithDeepEqual etc.) This makes them pollute Intellisense for all class objects, which is quite annoying.
I suggest refactoring to instead use non-extension methods, e.q. DeepEqual.IsEqual(ob1, ob2) etc.
found very flexible way to define comparison,
Implemented "flexible" way to compare 2 double with tolerance.
Probably, could you add 2 options (tolerance for double and float") by short way.
[Test]
public void DoubleTypeTesting()
{
const double expected = 5.0;
const double actual1 = 5.1;
const double actual2 = 5.2;
IComparison comparison = new MyComparison(0.15);
actual1.WithDeepEqual(expected)
.WithCustomComparison(comparison)
.Assert();
actual2.WithDeepEqual(expected)
.WithCustomComparison(comparison)
.Assert();
}
Good evening,
Apologies if this is addressed somewhere and I have missed it. DateTime value comparison doesn't appear to be happy, see below. Before finding DeepEqual I had to compare them after calling ToString() and comparing the string contents. Hopefully I'm just missing something
DeepEqual.Syntax.DeepEqualException : Comparison Failed: The following 2 differences were found.
Actual.DateCreated != Expected.DateCreated (9/7/2015 9:57:38 PM != 9/7/2015 9:57:38 PM)
Actual.LastModified != Expected.LastModified (9/7/2015 9:57:38 PM != 9/7/2015 9:57:38 PM)
Although it's not very common practice, sometimes, object models may provide a more concrete (down-casted) type of the same property as the base class, for the sake of convenience. The way to do this is to redefine the property, with the "new" keyword.
This fails with DeepEquals as it considers the property to be duplicated. Here's the repro:
[Fact]
public void when_comparing_object_with_new_property_then_fails()
{
var obj1 = new Derived { Schema = new DerivedSchema() };
var obj2 = new Derived { Schema = new DerivedSchema() };
obj1.IsDeepEqual(obj2);
}
public interface IBaseSchema { }
public interface IDerivedSchema : IBaseSchema { }
public class DerivedSchema : IDerivedSchema { }
public class Base
{
public IBaseSchema Schema { get; set; }
}
public class Derived : Base
{
// NOTE: the point here is to just make access to
// derived schema specific properties more convenient
// by avoiding down-casting everywhere, especially
// since this can be guaranteed to be of the given
// type.
public new IDerivedSchema Schema
{
get { return (IDerivedSchema)base.Schema; }
set { base.Schema = value; }
}
}
Since there's nothing 4.5-specific, it would be great if it was compatible with .NET 4.0 too :)
Given the example below, I would never expect the object Foo carrying the value 12 to equal 42.
It seems like a bug to me, and I'm afraid it'll result in many unexpected false positives.
[Fact]
public void ShouldFail()
{
new Foo(12).ShouldDeepEqual(42);
}
public class Foo
{
public int Value { get; }
public Foo(int value)
{
this.Value = value;
}
}
The NuGet package for DeepEqual is not strongly named, which means it cannot be loaded in a project that is signed. Could you sign the assembly and publish a strongly named package so that it can be referenced in signed assemblies?
There is no current way of extending the formatting of the exception message. You can easily add your own custom comparisons but if you need richer information in the output then you should be able to add a custom Difference
subclass and register a DifferenceFormatter
Is there a way to ignore comparison of double/float values if they are both NaN? Currently I see the comparison fail
Trying to run a property test using FsCheck. It generates also NaN for double values. Normally is always NaN != NaN, but for equality it doesn't matter. Is there any option for it or is it possible to convince DeepEqual to take NaNs as equal?
If I've missed some documentation which answers this, please let me know.
When doing a comparison, is it possible to ignore a property of an array element? E.g...
Instead of this:
actual.WithDeepEqual(expected)
.IgnoreSourceProperty(o => o.People[0].Id)
.IgnoreSourceProperty(o => o.People[1].Id)
...
.IgnoreSourceProperty(o => o.People[n].Id)
.Assert();
Do this:
actual.WithDeepEqual(expected)
.IgnoreSourceArrayProperty(o => o.People, o => o.Id)
.Assert();
That way, the comparison would ignore the Id
property for each element in the People
array on the source object.
It seems that by default instances of different enum types are never evaluated as deep-equal, even if their names match.
I have to apply SkipDefault<EnumType1>().SkipDefault<EnumType2>()
or WithCustomComparison(new EnumComparison())
to get a working comparison.
Is this the intended behavior?
public enum Foo { X = 1, Y = 2 }
public enum Bar { X = 1, Y = 2 }
[Test]
public void Enums_WithSameName_ShouldDeepEqual()
{
// throws with Actual != Expected (X != X)
(Foo.X).ShouldDeepEqual(Bar.X);
}
[Test]
public void Enums_WithSameName_WithDeepEqualAndSkipTypes_ShouldAssert()
{
// works (and successfully throws when comparing Foo.X with Bar.Y)
(Foo.X).WithDeepEqual(Bar.X).SkipDefault<Foo>().SkipDefault<Bar>().Assert();
}
[Test]
public void Enums_WithSameName_WithCustomComparison_ShouldAssert()
{
// works (and successfully throws when comparing Foo.X with Bar.Y)
(Foo.X).WithDeepEqual(Bar.X).WithCustomComparison(new EnumComparison()).Assert();
}
This is a recommendation that adding topics like comparer
equality
to the project description so people can reach here easily.
https://help.github.com/en/articles/classifying-your-repository-with-topics
TypeComparer
Returns failure when types don't match. Inconclusive otherwise.
NUnit:
Expected string length 3 but was 8. Strings differ at index 0.
Expected: "Joe"
But was: "Chandler"
-----------^
DeepEqual:
System.Exception : Comparison Failed: The following 1 differences were found.
Actual.Name != Expected.Name (Actual: "Chandler", Expected: "Joe")
[Test]
public void TestIgnoreByType()
{
var expected = new Data { Id = 5, Name = "Joe" };
expected.Items.Add(new DataItem { Name="1", Tag = new Tag()});
var actual = new Data { Id = 5, Name = "Joe" };
actual.Items.Add(new DataItem { Name = "1", Tag = new Tag()});
actual.WithDeepEqual(expected)
.Assert();
Assert.AreEqual(expected.Items[0].Tag.R, actual.Items[0].Tag.R);
}
public class Data
{
public int Id;
public string Name { get; set; }
public List<DataItem> Items = new List<DataItem>();
}
public class DataItem
{
public string Name { get; set; }
public Tag Tag { get; set; }
}
public class Tag
{
static readonly Random random = new Random();
public int R { get; set; }
public Tag()
{
R = random.Next();
Console.WriteLine("R={0}", R);
}
}
Hi,
Do you have any plan any plan to implement .NET Standard or .NET Core support?
Thanks.
Can I get difference between two objects without throw new DeepEqualException?
Method ‘ShouldDeepEqual’ returns good message with difference, but it creates exception.
If ObjA has 5 properties and ObjectB has 2 props, then compare should ignore not common props, obvious can't be equal something does not exists
In version 0.12.0.0 installed from NuGet, I get a StackOverflowException when an object graph contains cycles.
Sample below:
public class A
{
public List<B> Bees { get; set; }
}
public class B
{
public A A { get; set; }
}
public class C
{
public D D { get; set; }
}
public class D
{
public C C { get; set; }
}
class Program
{
static void Main(string[] args)
{
var a = new A();
var b = new B { A = a };
a.Bees = new List<B> { b };
var a1 = new A();
var b1 = new B { A = a1 };
a1.Bees = new List<B> { b1 };
a1.ShouldDeepEqual(a); //stack overflow exception
b1.ShouldDeepEqual(b); //stack overflow exception
var c = new C();
var d = new D { C = c };
c.D = d;
var c1 = new C();
var d1 = new D { C = c1 };
c1.D = d1;
c1.ShouldDeepEqual(c); //stack overflow exception
d1.ShouldDeepEqual(d); //stack overflow exception
}
}
After updating to DeepEqual 1.5.0, I have numerous errors stating: An expression tree may not contain a call or invocation that uses optional arguments
with lines like the following
_repository.Received().Update(Arg.Is<MyClass>(obj => obj.IsDeepEqual(expected)));
It's a NSubstitute test. I'm forced to add an extra NULL parameter to all my mock asserts:
_repository.Received().Update(Arg.Is<MyClass>(obj => obj.IsDeepEqual(expected, null)));
Could IsDeepEqual
use an overload instead of an optional parameter?
Thanks.
If a dynamic type properly implements GetDynamicMemberNames() then allow comparing such objects.
is it possible some option to compare enums via string match?
when there are two different classes with the same structure and content, comparison is ok, except enums
upd: seems possible with custom comparison, but nice to have option
Hi,
Great library, thanks for putting this together.
Do you know if it's possible to setup an exception to the user comparison of matching properties so that if a property has been renamed we can check the objects were copied properly ?
for example. imagine type 1 has a 'lastName' property, and type 2 has a 'surname' property.
Could we do something like 'mapProperty(x=> x.lastName, y=> y.surname)' ?
Thanks.
So that everyone gets the same CR/LF when sending pull requests
If my TestClass has public properties it works but when they are sat as internal DeepEquels no longer find any differences.
public class TestClass
{
internal int TestInt { get; set; }
internal string TestString { get; set; }
}
[TestFixture]
public class TestClassTest
{
[Test]
public void SerTest()
{
var testA = new TestClass() {TestInt = 1, TestString = "A"};
var testB = new TestClass() { TestInt = 2, TestString = "B" };
testA.ShouldDeepEqual(testB);
Assert.DoesNotThrow(() => testA.ShouldDeepEqual(testB));
Assert.IsTrue(testA.IsDeepEqual(testB));
}
}
is there some option for case-insensitive member name match?
i.e. Class1.ID match with Class2.id
also custom property matcher and/or regex pairs to match property names will be nice
with regex pairs: [/id/i, /(primary_?)?id/i] will match Class1.{id|ID|Id|...} to Class2.{id|ID|Id|PrimaryId|primary_id|...}
I have a use case where I want to compare two collections of entities (specific class type here is a custom in-house class, Answer
; it could be anything) and see if they are equal without concern for the order. The collections are IEnumerable
, so I can't count on a specific ordering, which also means I don't want my equality check to care about order. Right now, I've getting failures in DeepEqual where essentially it's telling me value1[0] != value2[0]
, but value1[1] == value2[0]
and vice versa for 2 two-element collections.
I need to fix that.
I'd hoped to create collections that are ordered (by Id
in this case) in my custom Compare
and then compare, but obviously the only really important idea is that they are matchable in some way that allows equality checks.
Right now I'm using code like the following in a custom IComparison
:
public (ComparisonResult result, IComparisonContext context) Compare(IComparisonContext context, object value1, object value2)
{
var custom1 = (IEnumerable<Answer>)value1;
var custom2 = (IEnumerable<Answer>)value2;
// Ob could add to the lines above
var ordered1 = custom1.ToList();
var ordered2 = custom2.ToList();
ordered1.Sort((a, b) => a.Id.CompareTo(b.Id));
ordered2.Sort((a, b) => a.Id.CompareTo(b.Id));
// I haven't found a good way to return the description.
// I can pull from the exception thrown by ShouldDeepEqual,
// but that apparently leaves the app in an residual
// exception-has-been-thrown state that gets ugly?
if (!ordered1.IsDeepEqual(ordered2))
{
context.AddDifference(value1, value2);
return (ComparisonResult.Fail, context);
}
return (ComparisonResult.Pass, context);
}
The comments kind of tell the tale. If I capture a throw (by using ShouldDeepEqual
instead of IsDeepEqual
), I get some text I might be able to output somewhere, but I didn't seem to be able to attach it to AddDifference
. So for the time being I'm checking the IsDeepEqual
boolean and returning the right ComparisonResult
value without a good message.
There is an AddDifference
overload that has a string as the third parameter, but that's for the field that's failing, and it didn't display text especially cleanly when it failed.
What's the right way to compare IEnumerable<T>
?
When attempting to ignore a nested property, it is still being considered during the comparison:
actual.WithDeepEqual(expected)
.IgnoreSourceProperty(o => o.SomeProperty.ChildProperty)
.Assert();
This still throws an exception when SomeProperty.ChildProperty
is different. Perhaps this is simply not supported?
Not an issue but more a question/suggestion. How hard would it be to add matching strategy, IgnorePropertiesMissingInDestination and IgnorePropertiesMissingInSource?
Found an issue - IsDeepEqueal() method returns false as the comparison result of exact KeyValuePair objects. That's working well when I'm using KeyValuePair with primitive types, but not working with my other defined types.
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
var model_1 = new KeyValuePair<string, Model>("1", new Model() { Id = 10, Name = "1" });
var model_2 = new KeyValuePair<string, Model>("1", new Model() { Id = 10, Name = "1" });
var model_3 = new KeyValuePair<int, string>(1, "1");
var model_4 = new KeyValuePair<int, string>(1, "1");
Console.WriteLine(model_1.IsDeepEqual(model_2));
Console.WriteLine(model_3.IsDeepEqual(model_4));
}
}
Hello
I am using the DeepEqual to test if the results of a test match my expected output.
I do the comparison simply as
results.ShouldDeepEqual(expected);
However, I don't know how to ignore a property within my list type
The type I am deep comparing contains a list. The data type held within this list contains a Guid property Id which I want to ignore, as well as a date.
Ignoring top level properties works fine. However, I don't see how to ignore properties on a list type.
To get around this for now, I have had to write some code to clear these properties myself, but this is obviously not ideal.
for (var i = 0; i < results.MyList.Count; i++)
{
results.MyList[i].Id = Guid.Empty;
expectedResults.MyList[i].Id = Guid.Empty;
}
How can I accomplish this?
When running the provided code the IsDeepEqual call consumes more than 1 GB of memory and had to be stopped after 1 minute.
using System;
using System.Collections.Generic;
using DeepEqual.Syntax;
namespace test1
{
class Test
{
public string A { get; set; }
}
class Program
{
static void Main(string[] args)
{
var object1 = new Dictionary<int, Test>();
var object2 = new Dictionary<int, Test>();
for (int c = 0; c < 100; c++)
{
object1.Add(c, new Test { A = c.ToString() });
object2.Add(c, new Test { A = c.ToString() });
}
object2[10].A = "different";
Console.WriteLine($"Equals: {object1.IsDeepEqual(object2)}");
}
}
}
This happens under .NET Core 3.1 and .NET 5 using version 2.0.0
I'm using DeepEqual for unit testing, and I would like to make a comparison of two class instances, that has a custom implementation of Equals, that compares only on a subset of the objects properties.
Is it possible to configure DeepEqual to not use the Equals method for comparison on class types (not strings), and thus always fall back to complex object comparisons for these?
And if not, would it be a welcome addition to the library?
Hi,
I want to use your library but I'm not able to ignore some properties in the following way (i guessed).
content.WithDeepEqual(expected).IgnoreSourceProperty(x => x.InfoContrat.PerfP).Assert();
This is the only property i don't want to compare.
Is this the good way to go ?
Where can i have some more documentation / examples ?
(my actual and expected objects are way too big to paste here)
The following test passed in 2.0.0 but fails in 4.2.1 as it returns a Pass now. Is this intended?
[Fact]
public void ComparingObjectsWithNoProperties_DifferentType()
{
var comparer = (IComparison)new ComparisonBuilder().Create();
var context = new ComparisonContext();
Assert.Equal(
ComparisonResult.Inconclusive,
comparer.Compare(context, new object(), new Blah()).result);
}
public class Blah { }
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.