FluentAssertions - how make ShouldBeEquivalentTo compare empty and null as equal - c#

I am using Fluent Assertion library as part of my unit tests for some custom serialization code and I am looking for a way to force ShouldBeEquivalentTo to compare as equal a null and empty list.
Basically, my tests look something like:
[Test]
public void Should_be_xxx()
{
ClassWithList one = new ClassWithList { Id = "ten", Items = null };
string serialized = Serialize(one);
ClassWithList two = Deserialize(serialized);
two.ShouldBeEquivalentTo(one);
}
However, one of the features of the Deserialize method is that if a collection type is missing in the input data, it sets the property on the deserialized class to an empty list, rather than null. So, very simplified, I end up with a situation where in instance two, Items = new List<string> rather than null.
Obviously, I could set one.Items = new List<string>() before comparing, but in reality I have a large number of complex domain objects that I am asserting in these methods and I am looking for a general solution. To put it another way, does anyone know how to make the following test pass:
public class ClassWithList
{
public string Id { get; set; }
public List<string> Items { get; set; }
}
[Test]
public void Should_be_xxx()
{
ClassWithList one = new ClassWithList { Id = "ten", Items = null };
ClassWithList two = new ClassWithList { Id = "ten", Items = new List<string>() };
two.ShouldBeEquivalentTo(one);
}
To put it another way, I am looking to apply the following test to all collections in a class X as part of comparing equivalence:
if (subject.Items == null)
{
expected.Items.Should().BeEmpty();
}
else
{
expected.Items.Should().BeEquivalentTo(subject.Items);
}

Based on the information from Dennis above, I was able to solve this will the following actual code:
public class ClassWithList
{
public string Id { get; set; }
public List<string> Items { get; set; }
public List<ClassWithList> Nested { get; set; }
}
[TestClass]
public class Test
{
[TestMethod]
public void Should_compare_null_to_empty()
{
ClassWithList one = new ClassWithList { Id = "ten", Items = null, Nested = new List<ClassWithList> { new ClassWithList { Id = "a" } } };
ClassWithList two = new ClassWithList { Id = "ten", Items = new List<string>(), Nested = new List<ClassWithList> { new ClassWithList { Id = "a", Items = new List<string>(), Nested = new List<ClassWithList> { } } } };
two.ShouldBeEquivalentTo(one, opt => opt
.Using<IEnumerable>(CheckList)
.When(info => typeof(IEnumerable).IsAssignableFrom(info.CompileTimeType)));
}
private void CheckList(IAssertionContext<IEnumerable> a)
{
if (a.Expectation == null)
{
a.Subject.Should().BeEmpty();
}
else
{
a.Subject.ShouldBeEquivalentTo(a.Expectation, opt => opt
.Using<IEnumerable>(CheckList)
.When(info => typeof(IEnumerable).IsAssignableFrom(info.CompileTimeType)));
}
}
}

You'll have to implement a custom 'IEquivalencyStep' or u.se 'options.Using(custom action).WhenTypeIs(predicate).

Create an IAssertionRule:
public class EnumerableNullEmptyEquivalenceRule : IAssertionRule
{
public bool AssertEquality(IEquivalencyValidationContext context)
{
// not applicable - return false
if (!typeof(IEnumerable).IsAssignableFrom(context.SelectedMemberInfo.MemberType)) return false;
return context.Expectation == null && ((IEnumerable)context.Subject).IsNullOrEmpty();
}
}
Then apply to your BeEquivalentTo call:
actual.Should().BeEquivalentTo(expected, opt => opt.Using(new EnumerableNullEmptyEquivalenceRule()));

Related

Merge list of objects using linq in C#

I have a class defined below
public class ResultClass()
{
public HashSet<string> VerifiedResults { get; private set; }
public HashSet<string> UnverifiedResults { get; private set; }
bool IsBlocked {get; private set; }
}
If I have a list containing items for ResultClass, for eg., List<ResultClass>() , how can I combine the results into a single ResultClass object using LINQ.
Is a way to do what my code below does, but with LINQ, instead?
var finalResult = new ResultClass();
foreach(var item in listOfResultClass)
{
finalResult.VerifiedResults.Union(item.VerifiedResults);
finalResult.UnverifiedResults.Union(item.UnverifiedResults);
finalResult.IsBlocked = item.IsBlocked;
}
This can be achieved using the LINQ Aggregate function, as can be seen in following example:
public class ResultClass
{
public HashSet<string> VerifiedResults { get; set; } = new();
public HashSet<string> UnverifiedResults { get; set; } = new();
public bool IsBlocked { get; set; } = false;
}
internal class Program
{
private static void Main()
{
var results = GetResults();
var finalResult = results.Aggregate(new ResultClass(), (r, next) =>
{
r.IsBlocked = r.IsBlocked || next.IsBlocked;
r.VerifiedResults.UnionWith(next.VerifiedResults);
r.UnverifiedResults.UnionWith(next.UnverifiedResults);
return r;
});
}
private static List<ResultClass> GetResults()
{
return new List<ResultClass>()
{
new ResultClass()
{
VerifiedResults = new HashSet<string>{"first", "second" },
UnverifiedResults = new HashSet<string>{"third" },
IsBlocked = false
},
new ResultClass()
{
VerifiedResults = new HashSet<string>{"first", "fourth" },
UnverifiedResults = new HashSet<string>{"fifth" },
IsBlocked = true
},
new ResultClass()
{
VerifiedResults = new HashSet<string> (),
UnverifiedResults = new HashSet<string>{"sixt", "seventh" },
IsBlocked = false
}
};
}
}
A few remarks:
I adapted your ResultClass to remove compiler errors and to make it possible to initialize a list of them easily.
for the 'IsBlocked property, I used the logical OR of all the individual IsBlocked properties.
You should use UnionWith rather than Union in this case.
It might be a good idea to think about possible conflicts where the same string occurs in different ResultClass instances, both in 'verified' and 'unverified' properties. It may be necessary to adapt the accumulator lambda expression to get the results you want.
As #iSR5 mentioned, you can use SelectMany. I am not sure how do you plan to assign the IsBlocked, but this seems to behave like your code:
var result = new ResultClass()
{
VerifiedResults = new HashSet<string>(results.SelectMany(x => x.VerifiedResults)),
UnverifiedResults = new HashSet<string>(results.SelectMany(x => x.UnverifiedResults)),
IsBlocked = results.LastOrDefault().IsBlocked,
};
Also, please read #Johan Donne comments.

Creating a list of an object which either does or doesn't exist in another different object list - C#

So I am trying to compare two different lists that both contain differently structured objects. One is easily accessible while the other is very nested in arrays, but sadly, these are responses from API calls so it's not a structure I can change to make them easier to compare. I want to have a list of the complete structures of items found:
var foundList = new List<Structure1>();
var notFoundList = new List<Structure1>();
The way the objects are structured are as follows:
public class ObjectsA
{
public Structure1[] Structure1 {get; set;}
}
public class Structure1
{
public int id {get; set;} //1
}
And the other object looks like:
public class ObjectsB
{
public Array1[] array1{get; set}
}
public class Array1
{
public Array2[] array2{get; set;}
}
public class Array2
{
public string id {get; set;} //"0001"
}
In a list, I added all the objects that came back from the API call, so ObjectAList contains technically just 1 deserialized object response, which contains an array of objects, while ObjectBList contains a list of objects added to it via AddRange.
At first I tried to putting an Array.Exists() inside of 2 foreach() statements.
foreach (var arr1 in ObjectsBList){
foreach (var arr2 in a.Array2){
if (Array.Exists(ObjectAList.Structure1, item => item.id == Convert.ToInt32(arr2.id)) == true){
foundList.AddRange(ObjectAList.Structure1);
}
else{
notFoundList.AddRange(ObjectAList.Structure1)
}}};
This code seemed to keep looping on the "item => item.id == Convert.ToInt32(arr2.id)" part of it, so consequently, it kept going till it found its match and so the answer was always 'true', therefore just putting everything in the foundList. I know I'm probably going at this wrong. I'm just starting out C# programming and I'm having trouble wrapping my mind around some of these things and knowing what all functions exists to help with what I need, etc. Any help would be great!
You can use linq for querying the in-memory objects.
pseudo code
public class Test {
public void T()
{
var ObjectsBList = new ObjectsB();
var ObjectAList = new ObjectsA();
var foundList = new List<Structure1>();
var notFoundList = new List<Structure1>();
var bList = ObjectsBList
.array1
.SelectMany(x => x.array2)
.Select(x => Convert.ToInt32(x.id))
.Distinct()
.ToList();
if (ObjectAList.Structure1.Any(x => bList.Contains(x.id)))
{
foundList.AddRange(ObjectAList.Structure1);
}
else
{
notFoundList.AddRange(ObjectAList.Structure1);
}
}
}
More simplified version:
Introduce an ID list property
using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;
public class ObjectsA
{
public Structure1[] Structure1 { get; set; }
public List<int> IDs
{
get
{
return Structure1.Select(x => x.id).Distinct().ToList();
}
}
}
public class Structure1
{
public int id { get; set; } //1
}
public class ObjectsB
{
public Array1[] array1 { get; set; }
public List<int> IDs
{
get
{
return array1
.SelectMany(x => x.array2)
.Select(x => Convert.ToInt32(x.id))
.Distinct()
.ToList();
}
}
}
public class Array1
{
public Array2[] array2 { get; set; }
}
public class Array2
{
public string id { get; set; } //"0001"
}
public class Test
{
public void T()
{
var ObjectsBList = new ObjectsB();
var ObjectAList = new ObjectsA();
var foundList = new List<Structure1>();
var notFoundList = new List<Structure1>();
if (ObjectAList.IDs.Any(x => ObjectsBList.IDs.Contains(x)))
{
foundList.AddRange(ObjectAList.Structure1);
}
else
{
notFoundList.AddRange(ObjectAList.Structure1);
}
}
}
var objA = new ObjectsA();
var objB = new ObjectsB();
var objAIds = objA.Structure1.Select(x => x.Id).Distinct();
var objBIds = objB.Array1.SelectMany(x => x.Array2).Select(x => int.Parse(x.Id)).Distinct();
var foundInBothList = objAIds.Intersect(objBIds);
var notFoundinBList = objAIds.Except(objBIds);
var inBoth = objA.Structure1.Where(x => foundInBothList.Contains(x.Id));
var notInB = objA.Structure1.Where(x => notFoundinBList.Contains(x.Id));
Starting from .NET 6
var objBIds = objB.Array1.SelectMany(x => x.Array2).Select(x => int.Parse(x.Id)).Distinct();
var foundList = objA.Structure1.IntersectBy(objBIds, x => x.Id);
var notFoundList = objA.Structure1.ExceptBy(objBIds, x => x.Id);

How can i add a property to object during the a run time at C#?

I have two different lists. I want to combine these list in an object. I don't know the property names in these lists, which is why I cannot create a class in advance.
public class exampleConfigProperty
{
public string PropertName { get; set; };
public string PropertValue { get; set; };
}
public class exampleFile
{
public string config1 { get; set; };
}
List<exampleConfigProperty> myList = new List<exampleConfigProperty>();
myList.add(new exampleConfigProperty {
PropertName = "Config_Property_Name",
PropertValue = "Config Property Value"
});
List<exampleFile> myListTwo = new List<exampleFile>();
myListTwo.add(new exampleFile {
config1 = "Config Value"
});
I tried to figure out if is there any way I can use the code below:
var obj = new object();
foreach(var item in myList)
{
obj.addProperty(item.PropertName, item.PropertValue );
}
foreach(var item in myListTwo)
{
obj.addProperty(nameof(item.config1), item.config1 );
}
In conclusion I try to create an object instance by using two lists,
for example:
var resut = new {
Config_Property_Name = "Config Property Value",
config1 ="Config Value"
}
dynamic type does not work so what should I do to get an object instance by using two lists?
Since you just want to make a JSON I think would fit your criteria:
var test = new Dictionary<string, string>(); //<string, object> for mixed types of values
test["key1"] = "value1";
test["key2"] = "value2";
var result = JsonConvert.SerializeObject(test); //indented for readability of result
// result:
// {
// "key1": "value1",
// "key2": "value2"
// }

CRUD over two different collections of POCOs

I have an interesting question, one I'm having difficulty searching for an answer on.
I have two IEnumerable collections of objects. The underlying objects are completely separate, BUT I can identify a shared key that should match. The collections are important, in that my "left" object is the "system of record", and the "right" object is representing a system I need to ensure matches the system of record.
Once they are matched, I need to perform CRUD operations on one side to bring the right side in line with the left side. For example, it would create a new item on the right side if one didn't exist, or update values, or delete if the item was missing on the left, but not the right.
The catch is, I have hundreds of these collections to match up, and the actual CRUD code is different.
I'd like to introduce some shared code where I can pass in both collections, the collection types (as probably generics), some kind of comparer, and some delegates of what operation to perform for CRUD.
If this code actually existed, it may look something like this
class Stuff
{
string Id {get; set;}
string Name {get; set;}
}
class Junk
{
string Id {get; set;}
string ShortName {get; set;}
}
IEnumerable<Stuff> myStuff = GetStuff();
IEnumerable<Junk> myJunk = GetJunk();
CrudComparer cc = new CrudComparer<Stuff, Junk>(myStuff, myJunk);
cc.Comparer = (leftObject, rightObject) => {
leftObject.Name == rightObject.Name
}
cc.CreateOperation = (newObject, rightCollection) => {
Junk j = new Junk();
j.Shortname = newObject.Name;
rightCollection.Add(j);
}
cc.UpdateOperation = (leftObject, rightObject) => {
rightObject.Shortname = leftObject.Name;
}
cc.DeleteOperation = (rightCollection, rightObject) => {
rightCollection.Remove(rightObject);
}
cc.Compare();
Has anyone ever seen code that does something like this? I'd hate to reinvent the wheel if I can grab something already done.
Thanks for any help!
--Michael
I got to thinking more about this, and realized what I knew about delgates and generics should be sufficient to solve this problem, so I got in LinqPad and had some fun. I haven't written any unit tests around this yet, so use at your own risk, but hopefully if you want to use this you understand the underlying concepts.
class Blah
{
public int ID { get; set; }
public string BlahName { get; set;}
}
class Bleh
{
public int ID { get; set; }
public string BlehName { get; set;}
}
class CrudComparer<TLeft, TRight>
{
private readonly ICollection<TLeft> _leftCollection;
private readonly ICollection<TRight> _rightCollection;
private readonly Comparer _compareOperation;
private readonly CreateOperation _createOperation;
private readonly UpdateOperation _updateOperation;
private readonly DeleteOperation _deleteOperation;
public delegate bool Comparer(TLeft leftItem, TRight rightItem);
public delegate void CreateOperation(TLeft leftItem, ICollection<TRight> rightCollection);
public delegate void UpdateOperation(TLeft leftItem, TRight rightItem);
public delegate void DeleteOperation(TRight rightItem, ICollection<TRight> rightCollection);
public CrudComparer(ICollection<TLeft> leftCollection, ICollection<TRight> rightCollection, Comparer compareOperation, CreateOperation createOperation, UpdateOperation updateOperation, DeleteOperation deleteOperation)
{
_leftCollection = leftCollection;
_rightCollection = rightCollection;
_compareOperation = compareOperation;
_createOperation = createOperation;
_updateOperation = updateOperation;
_deleteOperation = deleteOperation;
}
public void Compare()
{
foreach (TLeft leftItem in _leftCollection)
{
bool foundItem = false;
foreach (TRight rightItem in _rightCollection)
{
if (_compareOperation(leftItem, rightItem))
{
//these equal
foundItem = true;
}
}
if (foundItem == false)
{
_createOperation(leftItem, _rightCollection);
}
}
List<TRight> itemsToDelete = new List<TRight>();
foreach (TRight rightItem in _rightCollection)
{
bool foundItem = false;
foreach (TLeft leftItem in _leftCollection)
{
if (_compareOperation(leftItem, rightItem))
{
foundItem = true;
_updateOperation(leftItem, rightItem);
break;
}
}
if (!foundItem)
{
itemsToDelete.Add(rightItem);
}
}
foreach (TRight itemToDelete in itemsToDelete)
{
_deleteOperation(itemToDelete, _rightCollection);
}
}
}
void Main()
{
List<Blah> blahItems = new List<Blah>();
blahItems.Add(new Blah() { ID = 1, BlahName = "Blah" });
blahItems.Add(new Blah() { ID = 2, BlahName = "ABC" });
blahItems.Add(new Blah() { ID = 34, BlahName = "XYZ" });
blahItems.Add(new Blah() { ID = 6442, BlahName = "123" });
List<Bleh> blehItems = new List<Bleh>();
blehItems.Add(new Bleh() { ID = 2, BlehName = "12345"});
blehItems.Add(new Bleh() { ID = 6, BlehName = "43232"});
blehItems.Add(new Bleh() { ID = 77, BlehName = "BlahBlah"});
blehItems.Add(new Bleh() { ID = 2334, BlehName = "ZYX"});
CrudComparer<Blah, Bleh>.Comparer compareOperation = (leftObject, rightObject) =>
{
return leftObject.ID == rightObject.ID;
};
CrudComparer<Blah, Bleh>.CreateOperation createOperation = (leftObject, rightCollection) =>
{
rightCollection.Add(new Bleh() { ID = leftObject.ID });
};
CrudComparer<Blah, Bleh>.UpdateOperation updateOperation = (leftObject, rightObject) =>
{
rightObject.BlehName = leftObject.BlahName;
};
CrudComparer<Blah, Bleh>.DeleteOperation deleteOperation = (rightObject, rightCollection) =>
{
rightCollection.Remove(rightObject);
};
CrudComparer<Blah, Bleh> cc = new CrudComparer<Blah, Bleh>(blahItems, blehItems, compareOperation, createOperation, updateOperation, deleteOperation);
cc.Compare();
}

Build hierarchy from strings C#

I have a collection of strings:
"Alberton;Johannesburg"
"Allendale;Phoenix"
"Brackenhurst;Alberton"
"Cape Town;"
"Durban;"
"Johannesburg;"
"Mayville;Durban"
"Phoenix;Durban"
"Sandton;Johannesburg"
that I want to structure into a hierarchical structure in the fastest possible manner, like:
Johannesburg
Alberton
Brackenhurst
Sandton
Cape Town
Durban
Phoenix
Allandale
Mayville
Currently I have nested for loops and checks, but was hoping I could achieve this with a single LAMBDA query?
The above mentioned strings are in a List.
I prepared lambda-like solution, but you should really think if it's more readable/efficient then your current one:
Helper Extension Method:
public static class ChildrenGroupExtensions
{
public static List<CityInfo> GetChildren(this IEnumerable<IGrouping<string, City>> source, string parentName)
{
var cities = source.SingleOrDefault(g => g.Key == parentName);
if (cities == null)
return new List<CityInfo>();
return cities.Select(c => new CityInfo { Name = c.Name, Children = source.GetChildren(c.Name) }).ToList();
}
}
Helper Classes:
public class City
{
public string Name { get; set; }
public string Parent { get; set; }
}
public class CityInfo
{
public string Name { get; set; }
public List<CityInfo> Children { get; set; }
}
Usage:
var groups = (from i in items
let s = i.Split(new[] { ';' })
select new City { Name = s[0], Parent = s[1] }).GroupBy(e => e.Parent);
var root = groups.GetChildren(string.Empty);
Where items is your List<string>
You can look the results with simple helper method like that one:
private static void PrintTree(List<CityInfo> source, int level)
{
if (source != null)
{
source.ForEach(c =>
{
Enumerable.Range(1, level).ToList().ForEach(i => Console.Write("\t"));
Console.WriteLine(c.Name);
PrintTree(c.Children, level + 1);
});
}
}
And the results are:
Cape Town
Durban
Mayville
Phoenix
Allendale
Johannesburg
Alberton
Brackenhurst
Sandton
You haven't specified any specific data structure so I just used a class called Area with a list of children of itself. Also, it's in 2 lines of linq. There is also no check to see if an area is a child of 2 separate parents as the code is. Here's the code for the test I used(Relevant lines in-between the equals comments):
[TestFixture]
public class CitiesTest
{
[Test]
public void Test()
{
var strings = new List<string>
{
"Alberton;Johannesburg",
"Allendale;Phoenix",
"Brackenhurst;Alberton",
"Cape Town;",
"Durban;",
"Johannesburg;",
"Mayville;Durban",
"Phoenix;Durban",
"Sandton;Johannesburg"
};
//===================================================
var allAreas = strings.SelectMany(x=>x.Split(';')).Where(x=>!string.IsNullOrWhiteSpace(x)).Distinct().ToDictionary(x=>x, x=>new Area{Name = x});
strings.ForEach(area =>
{
var areas = area.Split(';');
if (string.IsNullOrWhiteSpace(areas[1]))
return;
var childArea = allAreas[areas[0]];
if (!allAreas[areas[1]].Children.Contains(childArea))
allAreas[areas[1]].Children.Add(childArea);
childArea.IsParent = false;
});
var result = allAreas.Select(x=>x.Value).Where(x => x.IsParent);
//===================================================
}
public class Area
{
public string Name;
public bool IsParent;
public List<Area> Children { get; set; }
public Area()
{
Children = new List<Area>();
IsParent = true;
}
}
}

Categories