C# - Asserting two objects are equal in unit tests
You've got two different Board
instances, so your call to Assert.AreEqual
will fail. Even if their entire contents appear to be the same, you're comparing references, not the underlying values.
You have to specify what makes two Board
instances equal.
You can do it in your test:
Assert.AreEqual(expected.Rows.Count, actual.Rows.Count);
Assert.AreEqual(expected.Rows[0].Cells[0], actual.Rows[0].Cells[0]);
// Lots more tests of equality...
Or you can do it in your classes: (note I wrote this on-the-fly - you'll want to adjust this)
public class Board
{
public List<Row> Rows = new List<Row>();
public override bool Equals(object obj)
{
var board = obj as Board;
if (board == null)
return false;
if (board.Rows.Count != Rows.Count)
return false;
return !board.Rows.Where((t, i) => !t.Equals(Rows[i])).Any();
}
public override int GetHashCode()
{
// determine what's appropriate to return here - a unique board id may be appropriate if available
}
}
public class Row
{
public List<int> Cells = new List<int>();
public override bool Equals(object obj)
{
var row = obj as Row;
if (row == null)
return false;
if (row.Cells.Count != Cells.Count)
return false;
if (row.Cells.Except(Cells).Any())
return false;
return true;
}
public override int GetHashCode()
{
// determine what's appropriate to return here - a unique row id may be appropriate if available
}
}
Compare two class objects in C#
Assert.Equal
relies on the object's .Equals
method (see here). When creating your own classes, you have an option to override this method and add your own logic of comparing two objects equality (e.g., field by field equality checking).
If you do not override it, there is a default .Equals
inherited from Object
, which is equivalent to .ReferenceEquals
: it returns true if the two objects have the same reference and false otherwise. The only situation when the two objects have the same reference is when it's the same object.
In your case, you can either define the .Equals
override for your class (e.g., see here) or you can try to follow some of the advice given in this SO answer on how to perform deep equality checking of two objects. Third option is to create a separate class inherited from IEqualityComparer
, which will purely provide the equality checking logic for your object while not being a part of your class codebase (for example, see this article).
How does Assert.AreEqual compare two objects in .net unit tests?
The official documentation is pretty laconical and doesn't explain it, so I believe that if the objects are not primitives, then their references will be compared.
That is, two references to the same object would evaluate as being equal; two clones of a same object would evaluate as being different. Unless you overload the Equals()
instance method of the class(es) those objects belong to, or the ==
operator for said class(es).
Also see Reed Copsey's answer.
assert that all fields in 2 objects are the same c#
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
How to test equality of two objects in unit tests?
Your second approach is much better than the other two, because it keeps tests-specific code together with unit tests, and also because it lets you change your definition of what it means for two View-Models to be equal.
It's hardly worth it to move equality comparers into a separate project, though: if you intend to share equality comparison logic, then you might as well put it into your objects. If, on the other hand, it is designed only for testing, then you should keep it together with the tests (i.e. use your approach #1).
The approach based on serialization is too fragile, because it relies on an unrelated feature (i.e. serialization) to test the feature that you are actually testing (i.e. conversion).
C# Comparing Multiple Objects in Unit Testing without Overriding
So as not to override the Equals
method, you would typically create an implementation of IEqualityComparer<T>
where T
is the type you want to compare, in your case Product
.
In the comparer you have to implement the bool Equals(T x, T y)
and int GetHashCode(T obj)
methods.
Yours may look something like:
public class ProductComparer : IEqualityComparer<Product>
{
// Implement Equals and GetHashCode
}
Then you can use it like this:
var actual = new List<int>();
var expected = new List<int>();
var comparer = new ProductComparer();
Assert.That(actual, Is.EqualTo(expected).Using(comparer));
Assert to compare two lists of objects C#
Just incase someone comes across this in the future, the answer was I had to create an Override, IEqualityComparer as described below:
public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
if (object.ReferenceEquals(x, y)) return true;
if (object.ReferenceEquals(x, null)||object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name && x.Age == y.Age;
}
public int GetHashCode(MyPerson obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
int hasCodeAge = obj.Age.GetHashCode();
return hashCodeName ^ hasCodeAge;
}
}
How to test if two collections of objects are in a different order based on one of the objects' properties?
To determine if the cards are in the same order by using the Id
property, you can simply select that property into an IEnumerable
and then use the System.Linq
extension method SequenceEqual
on them:
// Where 'unshuffled' and 'shuffled' are both 'List<Card>' objects
var areEqual = unshuffled.Select(c => c.Id).SequenceEqual(shuffled.Select(c => c.Id));
Alternatively, you could overried Equals
and GetHashCode
on the Card
class, and then use that for the equality comparison. This will help with other card comparisons that may be needed:
public class Card : IEquatable<Card>
{
public int Id { get; set; }
public bool Equals(Card other)
{
return Id == other?.Id;
}
public override bool Equals(object obj)
{
return Equals(obj as Card);
}
public override int GetHashCode()
{
return Id;
}
}
Now our comparison code is slightly more simple:
var areEqual = unshuffled.SequenceEqual(shuffled);
How to test two objects in xunit using FluentAssertions
Looking at your usage of tuples, you're using a recent .NET version. This gives you access to record
, which I recommend you use for your Cell
structure.
public record struct Cell (char Value);
Records come with an implied constructor and equality comparison. The equality comparison is important for you, because to compare two Matrix
objects, you would need to make sure the content of their Cell[,]
are identical.
You now have two options:
1. Should().Equal()
Works on IEnumerable
. So if you want to compare two Matrix
objects, you want to expose the contents of your Cell[,]
arrays and compare the two. You use Equal()
because you want to compare the order of items. Don't use BeEquivalentTo()
because it doesn't care about order. See here for the different enumerable comparisons.
public class Matrix {
...
// Expose your array as an IEnumerable
public IEnumerable<Cell> Cells => _matrix.OfType<Cell>();
}
// And compare the contents in sequence
public void Test(){
// These have 1 Mine in the same location:
Matrix m1 = new(4,4); m1.SetMine(new(1,1));
Matrix m2 = new(4,4); m2.SetMine(new(1,1));
// 1 Mine in a different location:
Matrix xx = new(4,4); xx.SetMine(new(2,2));
// Same contents in the same order
m2.Cells.Should().Equal(m1.Cells);
// Same contents different order, are NOT Equal
xx.Cells.Should().NotEqual(m1.Cells);
// But are Equivalent
xx.Cells.Should().BeEquivalentTo(m1.Cells);
}
2. Should().Be()
If you want to be strict with encapsulation, and don't want to expose the contents of your Cell[,]
as public, then you're going to have to define your equality comparison between 2 Matrix
objects. This allows you to use Be()
which will call the object's Equals()
method.
(Here's a full fiddle of the code below)
public class Matrix {
...
/* Easiest way to compare two Cell collections is to join
the chars into a string, and perform string comparison.
We could go through the two arrays and compare the contents
one by one, but a good programmer is a lazy one.
*/
private string CellsAsString() // private so the public can't see it
=> string.Concat(_matrix.OfType<Cell>().Select(c => c.Value));
/* Now Override the Equality comparison of the Object */
public override bool Equals(object other)
=> this.CellsAsString().Equals((other as Matrix)?.CellsAsString());
// Note that we can access private methods of another object
// as long as it's the same class as `this`.
}
Your comparison will now simply be:
Matrix m1 = new(4,4); m1.SetMine(new(1,1));
Matrix m2 = new(4,4); m2.SetMine(new(1,1));
Matrix xx = new(4,4); xx.SetMine(new(2, 2));
m2.Should().Be(m1);
xx.Should().NotBe(m1);
Related Topics
How to Programmatically Create, Read, Write an Excel Without Having Office Installed
Calling SQL Defined Function in C#
C# Structs: Unassigned Local Variable
Creating a Database Programmatically in SQL Server
How to Connect to a Ms Access File (Mdb) Using C#
C# Regex: Checking for "A-Z" and "A-Z"
Why Is Webbrowser_Documentcompleted() Firing Twice
Very Simple Definition of Initializecomponent(); Method
Text Property in a Usercontrol in C#
Specified Key Is Not a Valid Size for This Algorithm
Mocking Static Methods Using Rhino.Mocks
Selecting Attribute Values with HTML Agility Pack
Is Using an an 'Async' Lambda with 'Task.Run()' Redundant
Looking for C# Equivalent of Scanf
Opening Process and Changing Window Position
Get List<> Element Position in C# Using Linq