FluentAssertions; combining collection and object graph comparison assertions - c#

I am trying to use FluentAssertions to combine collection and object graph comparison assertions.
I have the following class.
public class Contract
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Which are returned in a collection, like so.
ICollection<Contract> contracts = factory.BuildContracts();
I then want to make sure that collection contains only specific Contract objects.
contracts.Should().Contain(new Contract() { Id = id1, Name = "A" });
This doesn't work, I'm believe because Contain is using object.Equals rather than object graph comparison, (as provided by ShouldBeEquivalentTo).
I also need to assert that the collection doesn't contain a specific object, i.e.
contracts.Should().NotContain(new Contract() { Id = id2, Name = "B" });
Effectively given a collection containing an unknown number of items, I want to ensure that; it contains a number of specific items, and that it doesn't contain a number of specific items.
Can this be achieved using the functions provided by FluentAssertions?
As a side note, I don't want to override object.Equals for the reasons discussed here. Should I be using IEquatable to ease testing of factories?

It does use the object.Equals as far as I can tell from documentation and my experience using the framework.
In scenarios like this I tend to use an expression predicate as referenced in the collections documentation for v3.0 and higher.
The following example shows how to make sure that collection contains only specific Contract objects and also assert that the collection doesn't contain a specific object.
[TestMethod]
public void FluentAssertions_Should_Validate_Collections() {
//Arrange
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var list = new List<Contract>{
new Contract() { Id = id1, Name = "A" },
new Contract() { Id = Guid.NewGuid(), Name = "B"}
};
var factoryMock = new Mock<IContractFactory>();
factoryMock.Setup(m => m.BuildContracts()).Returns(list);
var factory = factoryMock.Object;
//Act
var contracts = factory.BuildContracts();
//Assert
contracts.Should()
.HaveCount(list.Count)
.And.Contain(c => c.Id == id1 && c.Name == "A")
.And.NotContain(c => c.Id == id2 && c.Name == "B");
}

You can override your contract Equals which will then be used and your Unit test should pass happily.
If you're using random Guids it might be an issue, but it seems like you're using a predefined one.
Give the following a try:
protected bool Equals(Contract other)
{
return Id.Equals(other.Id) && string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Contract) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Id.GetHashCode()*397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
and as you can see, it passes the test:

Alternatively to Nkosi's answer, you could still use ShouldBeEquivalentTo by building the expecation.

Related

HashSet initialized using IEnumerable contains duplicate elements

I want to be able to use HashSet constructed from IEnumerable collection of custom objects without duplicates. My custom object contains id and some other properties which aren't important for this question. I make a query to a database which returns an IEnumerable that I later use to construct a HashSet with the following code:
HashSet<Question> results = new HashSet<Question>(new QuestionComparer());
var result = await query.ExecuteNextAsync<Question>();
results.UnionWith(result);
The problem is there are duplicate records inside the result collection which I do not want.
The QuestionComparer class looks like this:
public class QuestionComparer : IEqualityComparer<Question>
{
public bool Equals(Question x, Question y)
{
return x != null && y != null && x.Id == y.Id;
}
public int GetHashCode(Question obj)
{
return obj.Id.GetHashCode();
}
}
I also tried overriding both Equals and GetHashCode methods inside the Question class but no success. I considered looping through the collection and removing the duplicates, but it seems like it may become a performance problem...
EDIT: Azure DocumentDB that I am using apparently does not currently support a "distinct" type of query.
Instead of writing a public class QuestionComparer you should override the methods of the existing Question class
public class Question
{
public string ID { get; set; }
public override int GetHashCode()
{
return ID.GetHashCode();
}
public override bool Equals(System.Object obj)
{
return (obj != null && obj is Question) ? (this.ID == ((Question)(obj)).ID) : false;
}
}
So duplicates are not possible. Sample:
HashSet<Question> qh = new HashSet<Question>();
qh.Add(new Question() { ID = "1" });
qh.Add(new Question() { ID = "1" }); //will not be added
qh.Add(new Question() { ID = "2" });
https://dotnetfiddle.net/wrFTaA

C# ASP.Net MVC - Unit Test Assert Method Fail

So a quick rundown about the program I am testing. The program is a small app that register athletes' track records from all kinds of track meet. Right now I am testing the functionality of the option to create a new meet "CreateMeet". Every meet have different type of events: track, javelin, etc... and it takes place at specific time and location.
I wrote a small test function that allows me to test the creation of a new meet into the database, and so far the test failed because according to the test results, the problem is in my Assert function. I was sure that it would work but I guess it didn't. The test failure error gives me this:
Test Failed - CreateMeet
Message: Assert.IsTrue failed.
Below is the test function:
public void CreateMeet()
{
var createMeet = new Meet()
{
MeetId = 1234,
MeetName = Spring Meet,
MeetDate = DateTime.Now,
MeetLocation = "Paris, France",
MeasurementType = "Metric"
};
var MeetController = new MeetsController();
MeetController.AddMeet(createMeet);
var testList = new List<Meet>(MeetController.RetrieveAllMeets());
Assert.IsTrue(testList.Contains(createMeet));
}
Which Assert or which type of function do you guys think I may need for this function to generate a successful test? I'm stumped on any other options. Your input is appreciated.
UPDATE
Additional code as per D-Shih's request.
public IList<Meet> RetrieveAllMeets()
{
JObject jsonMeetObj = JObject.Parse(FieldScribeAPIRequests.FieldScribeGETAsync(FieldScribeAPIRequests.FieldScribeAPIRootAddress + "meets"));
IList<JToken> results = jsonMeetObj["value"].Children().ToList();
IList<Meet> meets = new List<Meet>();
foreach (JToken item in results)
meets.Add(item.ToObject<Meet>());
return meets;
}
I believe you're comparing instances, when you want to compare values.
Briefly, the object 'createMeet' is not the same object as will be found in 'testList' - they are of the same class, and they hold the same data values, but they are not the same instance of an object.
To get it to do what you want, you could build a CompareTo(Meet) method on your DTO that compares all the field values, and returns a true/false that the objects are 'data equivalent'.
-- Edit: further info.
Consider the following class
public class DTO
{
public int Value;
public bool CompareTo(DTO Target)
{
return (Target.Value == Value);
}
}
Now, consider this code:
private void button1_Click(object sender, EventArgs e)
{
DTO a = new DTO();
DTO b = new DTO();
a.Value = 1;
b.Value = 1;
if (a == b)
MessageBox.Show("Should not happen");
if (a.CompareTo(b))
MessageBox.Show("Should happen");
}
Variables a and b are data equivalent; they mean the same thing. But they are not the same instance of an object. So, the first if statement fails, and you will not see "Should not happen", but the second if passes, and you will see "Should happen".
You could override the == operator like below, but I wouldn't recommend it:
public class DTO
{
public int Value;
public bool CompareTo(DTO Target)
{
return (Target.Value == Value);
}
public static bool operator ==(DTO a, DTO b)
{
return (a.Value == b.Value);
}
public static bool operator !=(DTO a, DTO b)
{
return (a.Value != b.Value);
}
}
From your question your expect might want to compare object's fields from Meet Class.
Contain method in List which will be to check object exist by Equals method.
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).
Meet is a reference type,When you used Contains method will compare object's pointer.
A Simple way to do override Equals(object obj) on Meet
public class Meet
{
public int MeetId { get; set; }
public string MeetName { get; set; }
public DateTime MeetDate { get; set; }
public string MeetLocation { get; set; }
public string MeasurementType { get; set; }
public override bool Equals(object obj)
{
Meet input = obj as Meet;
if (obj==null)
return false;
return MeetId == input.MeetId &&
MeetName == input.MeetName &&
MeetDate == input.MeetDate &&
MeetLocation == input.MeetLocation &&
MeasurementType == input.MeasurementType;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
Since you are storing meet with With Id , instead of retrieving all meets and
checking if the collection contains the meet, write a method to get meet by meet id. Assert that it returns a meet and all properties that you are expecting do match. Take a look at fluent assertions which can make things simpler for you when it comes to assertions. http://fluentassertions.com/
Also, since you mentioned is as "Unit test" you should consider mocking to create a kind of in-memory collection and test against them instead of testing against Database.

Value-equals and circular references: how to resolve infinite recursion?

I have some classes that contain several fields. I need to compare them by value, i.e. two instances of a class are equal if their fields contain the same data. I have overridden the GetHashCode and Equals methods for that.
It can happen that these classes contain circular references.
Example: We want to model institutions (like government, sports clubs, whatever). An institution has a name. A Club is an institution that has a name and a list of members. Each member is a Person that has a name and a favourite institution. If a member of a certain club has this club as his favourite institution, we have a circular reference.
But circular references, in conjunction with value equality, lead to infinite recursion. Here is a code example:
interface IInstitution { string Name { get; } }
class Club : IInstitution
{
public string Name { get; set; }
public HashSet<Person> Members { get; set; }
public override int GetHashCode() { return Name.GetHashCode() + Members.Count; }
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
return Name.Equals(other.Name) && Members.SetEquals(other.Members);
}
}
class Person
{
public string Name { get; set; }
public IInstitution FavouriteInstitution { get; set; }
public override int GetHashCode() { return Name.GetHashCode(); }
public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null)
return false;
return Name.Equals(other.Name)
&& FavouriteInstitution.Equals(other.FavouriteInstitution);
}
}
class Program
{
public static void Main()
{
Club c1 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p1 = new Person { Name = "Johnny", FavouriteInstitution = c1 }
c1.Members.Add(p1);
Club c2 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p2 = new Person { Name = "Johnny", FavouriteInstitution = c2 }
c2.Members.Add(p2);
bool c1_and_c2_equal = c1.Equals(c2); // StackOverflowException!
// c1.Equals(c2) calls Members.SetEquals(other.Members)
// Members.SetEquals(other.Members) calls p1.Equals(p2)
// p1.Equals(p2) calls c1.Equals(c2)
}
}
c1_and_c2_equal should return true, and in fact we (humans) can see that they are value-equal with a little bit of thinking, without running into infinite recursion. However, I can't really say how we figure that out. But since it is possible, I hope that there is a way to resolve this problem in code as well!
So the question is: How can I check for value equality without running into infinite recursions?
Note that I need to resolve circular references in general, not only the case from above. I'll call it a 2-circle since c1 references p1, and p1 references c1. There can be other n-circles, e.g. if a club A has a member M whose favourite is club B which has member N whose favourite club is A. That would be a 4-circle. Other object models might also allow n-circles with odd numbers n. I am looking for a way to resolve all these problems at once, since I won't know in advance which value n can have.
An easy workaround (used in RDBMS) is to use a unique Id to identify a Person(any type). Then you don't need to compare every other property and you never run into such cuircular references.
Another way is to compare differently in Equals, so provide the deep check only for the type of the Equals and not for the referenced types. You could use a custom comparer:
public class PersonNameComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
if(object.ReferenceEquals(x, y)) return true;
return x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj?.Name?.GetHashCode() ?? int.MinValue;
}
}
Now you can change the Equals implementation of Club to avoid that the Members(Persons) will use their deep check which includes the institution but only their Name:
public override bool Equals(object obj)
{
if (Object.ReferenceEquals(this, obj))
return true;
Club other = obj as Club;
if (other == null)
return false;
var personNameComparer = new PersonNameComparer();
return Name.Equals(other.Name)
&& Members.Count == other.Members.Count
&& !Members.Except(other.Members, personNameComparer).Any();
}
You notice that i can't use SetEquals because there is no overload for my custom comparer.
Following the suggestion of Dryadwoods, I changed the Equals methods so that I can keep track of the items that were already compared.
First we need an equality comparer that checks reference equality for corresponding elements of pairs:
public class ValuePairRefEqualityComparer<T> : IEqualityComparer<(T,T)> where T : class
{
public static ValuePairRefEqualityComparer<T> Instance
= new ValuePairRefEqualityComparer<T>();
private ValuePairRefEqualityComparer() { }
public bool Equals((T,T) x, (T,T) y)
{
return ReferenceEquals(x.Item1, y.Item1)
&& ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode((T,T) obj)
{
return RuntimeHelpers.GetHashCode(obj.Item1)
+ 2 * RuntimeHelpers.GetHashCode(obj.Item2);
}
}
And here is the modified Equals method of Club:
static HashSet<(Club,Club)> checkedPairs
= new HashSet<(Club,Club)>(ValuePairRefEqualityComparer<Club>.Instance);
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
if (!Name.Equals(other.Name))
return;
if (checkedPairs.Contains((this,other)) || checkedPairs.Contains((other,this)))
return true;
checkedPairs.Add((this,other));
bool membersEqual = Members.SetEquals(other.Members);
checkedPairs.Clear();
return membersEqual;
}
The version for Person is analogous. Note that I add (this,other) to checkedPairs and check if either (this,other) or (other,this) is contained because it might happen that after the first call of c1.Equals(c2), we end up with a call of c2.Equals(c1) instead of c1.Equals(c2). I am not sure if this actually happens, but since I can't see the implementation of SetEquals, I believe it is a possibility.
Since I am not happy with using a static field for the already checked pairs (it will not work if the program is concurrent!), I asked another question: make a variable last for a call stack.
For the general case that I am interested in
-- where we have classes C1, ..., Cn where each of these classes can have any number of VALUES (like int, string, ...) as well as any number of REFERENCES to any other classes of C1, ..., Cn (e.g. by having for each type Ci a field ICollection<Ci>) --
the question "Are two objects A and B equal?", in the sense of equality that I described here,
seems to be EQUIVALENT to
the question "For two finite, directed, connected, colored graphs G and H, does there exist an isomorphism from G to H?".
Here is the equivalence:
graph vertices correspond to objects (class instances)
graph edges correspond to references to objects
color corresponds to the conglomerate of values and the type itself (i.e. colors of two vertices are the same if their corresponding objects have the same type and the same values)
That's an NP-hard question, so I think I'm going to discard my plan to implement this and go with a circular-reference-free approach instead.

Can I use LINQ to compare what is missing, added or updated between two collections

I have the following class. To make it so I could compare I added an Equals method:
public ObjectiveDetail()
public int ObjectiveDetailId { get; set; }
public int Number { get; set; }
public string Text { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as ObjectiveDetail);
}
public bool Equals(ObjectiveDetail other)
{
if (other == null)
return false;
return this.Number.Equals(other.Number) &&
(
this.Text == other.Text ||
this.Text != null &&
this.Text.Equals(other.Text)
);
}
}
I have two ICollection collections:
ICollection<ObjectiveDetail> _obj1; // Reference
ICollection<ObjectiveDetail> _obj2; // May have more, less or different objectDetails from the reference.
The common tfield with the collections is ObjectiveDetailId. Is there a way I can use three LINQ expressions to create:
A collection of rows in _obj2 and not _obj1
A collection of rows in _obj1 and not _obj2
A collection of rows different between _obj1 and _obj2
Note this is similar to another question I asked earlier but I think this is a bit simpler now I have added the Equals method. uld do this?
You can use Except to subtract sets:
var in2butNot1 = _obj2.Except(_obj1);
var in1butNot2 = _obj1.Except(_obj2);
However, this may not be what you are looking to get, because objects that have "changed" will be treated as simply "not equal" to each other.
It appears that your objects have an ID field. You can order the objects on that ID, and then traverse both collections as if you were producing a merge. This would let you detect insertions, updates, and deletions with a straightforward chain of ifs.
You can also use IDs to decide what's common and what's changed:
var ids1 = new HashSet<int>(_obj1.Select(o => o.ObjectiveDetailId));
var ids2 = new HashSet<int>(_obj2.Select(o => o.ObjectiveDetailId));
var in2butNot1 = _obj2.Where(o => !ids1.Contains(o.ObjectiveDetailId));
var in1butNot2 = _obj1.Where(o => !ids2.Contains(o.ObjectiveDetailId));
You should always override Equals and GetHashCode:
A collection of rows in _obj2 and not _obj1
var inObj2NotInObj1 = _obj2.Except(_obj1).ToList();
A collection of rows in _obj1 and not _obj2
var inObj1NotInObj2 = _obj1.Except(_obj2).ToList();
A collection of rows different between _obj1 and _obj2
Specify different, if you mean not Equals, that is what you have above.
What I mean with not Equals is when the objects have the same
ObjectiveDetailId but different "Number" or "Text" field values.
If you create a dictionary which maps an ID to the original (_obj1) objects, you could then look up the original with a matching ID for each new (_obj2) object and compare:
var oldDictionary = _obj1.ToDictionary(old => old.ObjectiveDetailId);
var updated = _obj2.Where(current => {
ObjectiveDetail old = null;
var isExisting = oldDictionary.TryGetValue(current.ObjectiveDetailId, out old);
return isExisting && !old.Equals(current);
});

Comparing two lists and ignoring a specific property

I have two employee lists that I want to get only unique records from but this has a twist to it. Each list has an Employee class in it:
public class Employee
{
// I want to completely ignore ID in the comparison
public int ID{ get; set; }
// I want to use FirstName and LastName in comparison
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
The only properties I want to compare on for a match are FirstName and LastName. I want to completely ignore ID in the comparison. The allFulltimeEmployees list has 3 employees in it and the allParttimeEmployees list has 3 employees in it. The first name and last name match on two items in the lists - Sally Jones and Fred Jackson. There is one item in the list that does not match because FirstName is the same, but LastName differs:
emp.id = null; // not populated or used in comparison
emp.FirstName = "Joe"; // same
emp.LastName = "Smith"; // different
allFulltimeEmployees.Add(emp);
emp.id = 3; // not used in comparison
emp.FirstName = "Joe"; // a match
emp.LastName = "Williams"; // not a match - different last name
allParttimeEmployees.Add(emp);
So I want to ignore the ID property in the class during the comparison of the two lists. I want to flag Joe Williams as a non-match since the last names of Smith and Williams in the two lists do not match.
// finalResult should only have Joe Williams in it
var finalResult = allFulltimeEmployees.Except(allParttimeEmployees);
I've tried using an IEqualityComparer but it doesn't work since it is using a single Employee class in the parameters rather than an IEnumerable list:
public class EmployeeEqualityComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
if (x.FirstName == y.FirstName && x.LastName == y.LastName)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Employee obj)
{
return obj.GetHashCode();
}
}
How can I successfully do what I want and perform this operation? Thanks for any help!
Your idea of using the IEqualityComparer is fine, it's your execution that is wrong. Notably, your GetHashCode method.
public int GetHashCode(Employee obj)
{
return obj.GetHashCode();
}
IEqualityComparer defines both Equals and GetHashCode because both are important. Do not ignore GetHashCode when you implement this interface! It plays a pivotal role on equality comparisons. No, it is not an indication that two items are equal, but it is an indicator that two elements are not. Two equal elements must return the same hash code. If they do not, they cannot be considered equal. If they do, then they might be equal, and equality functions only then go on to explore Equals.
With your implementation delegating to the GetHashCode method of the actual employee object, you are relying upon the implementation that Employee class uses. Only if that implementation is overriden will it be useful for you, and only if it is using your key fields. And if it is, then it is very likely that you did not need to define your own external comparer in the first place!
Build a GetHashCode method that factors in your key fields and you will be set.
public int GetHashCode(Employee obj)
{
// null handling omitted for brevity, but you will want to
// handle null values appropriately
return obj.FirstName.GetHashCode() * 117
+ obj.LastName.GetHashCode();
}
Once you have this method in place, then use the comparer in your call to Except.
var comparer = new EmployeeEqualityComparer();
var results = allFulltimeEmployees.Except(allParttimeEmployees, comparer);
You can override Equals and GetHashCode in your Employees class.
For example,
public class Employee
{
// I want to completely ignore ID in the comparison
public int ID { get; set; }
// I want to use FirstName and LastName in comparison
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
var other = obj as Employee;
return this.FirstName == other.FirstName && this.LastName == other.LastName;
}
public override int GetHashCode()
{
return this.FirstName.GetHashCode() ^ this.LastName.GetHashCode();
}
}
I tested with the following data set:
var empList1 = new List<Employee>
{
new Employee{ID = 1, FirstName = "D", LastName = "M"},
new Employee{ID = 2, FirstName = "Foo", LastName = "Bar"}
};
var empList2 = new List<Employee>
{
new Employee { ID = 2, FirstName = "D", LastName = "M" },
new Employee { ID = 1, FirstName = "Foo", LastName = "Baz" }
};
var result = empList1.Except(empList2); // Contained "Foo Bar", ID #2.
your IEqualityComparer should work:
var finalResult = allFulltimeEmployees.Except(allParttimeEmployees, new EmployeeEqualityComparer());
Try implementing the IEquatable(T) interface for your Employee class. You simply need to provide an implementation for an Equals() method, which you can define however you want (i.e. ignoring employee IDs).
The IEquatable interface is used by generic collection objects such
as Dictionary, List, and LinkedList when testing
for equality in such methods as Contains, IndexOf, LastIndexOf, and
Remove. It should be implemented for any object that might be stored
in a generic collection.
Example implementation of the Equals() method:
public bool Equals(Employee other)
{
return (other != null) && (FirstName == other.FirstName) && (LastName == other.LastName);
}
It's not the most elegant solution, but you could make a function like so
public string GetKey(Employee emp)
{
return string.Format("{0}#{1}", emp.FirstName, emp.LastName)
}
and then populate everything in allFullTimeEmployees into a Dictionary<string, Employee> where the key of the dictionary is the result of calling GetKey on each employee object. Then you could loop over allParttimeEmployees and call GetKey on each of those, probing into the dictionary (e.g. using TryGetValue or ContainsKey), and taking whatever action was necessary on a duplicate, such as removing the duplicate from the dictionary.

Categories