Issues in Xunit.Assert.Collection - C#
Asked Answered
B

3

29

I have a Class Library, it contains the following Model and Method

Model:

public class Employee {
    public int EmpId { get; set; }
    public string Name { get; set; }
}

Method:

public class EmployeeService {
    public List<Employee> GetEmployee() {
        return new List<Employee>() {
            new Employee() { EmpId = 1, Name = "John" },
            new Employee() { EmpId = 2, Name = "Albert John" },
            new Employee() { EmpId = 3, Name = "Emma" },
        }.Where(m => m.Name.Contains("John")).ToList();
    }
}

I have a Test Method

[TestMethod()]
public void GetEmployeeTest() {
    EmployeeService obj = new EmployeeService();
    var result = obj.GetEmployee();
    Xunit.Assert.Collection<Employee>(result, m => Xunit.Assert.Contains("John",m.Name));
}

I got an Exception message

Assert.Collection() Failure
Collection: [Employee { EmpId = 1, Name = "John" }, Employee { EmpId = 2, Name = "Albert John" }]
Expected item count: 1
Actual item count:   2

My requirement is to check all the items.Name should contain the sub string "John". Kindly assist me how to check using Xunit.Assert.Collection

Belligerence answered 9/5, 2017 at 15:44 Comment(0)
E
76

It appears that Assert.Collection only uses each element inspector once. So, for your test, the following works:

If the sequence result has exactly two elements:

[Fact]
public void GetEmployeeTest()
{
    EmployeeService obj = new EmployeeService();
    var result = obj.GetEmployee();

    Assert.Collection(result, item => Assert.Contains("John", item.Name),
                              item => Assert.Contains("John", item.Name));
}

If there are many elements, changing the Assert to

Assert.All(result, item => Assert.Contains("John", item.Name));

should give you the result you are expecting.

Edict answered 3/6, 2017 at 14:42 Comment(0)
O
4

This is an expansion on Ayb4btu's answer for those who aren't interested in the order of items in the collection.

The following method is based on the original XUnit implementation, and will allow you to test using a very similar interface:

public static class TestExpect
{
public static void CollectionContainsOnlyExpectedElements<T>(IEnumerable<T> collectionToTest, params Func<T, bool>[] inspectors)
{
    int expectedLength = inspectors.Length;
    T[] actual = collectionToTest.ToArray();
    int actualLength = actual.Length;

    if (actualLength != expectedLength)
        throw new CollectionException(collectionToTest, expectedLength, actualLength);

    List<Func<T, bool>> allInspectors = new List<Func<T, bool>>(inspectors);
    int index = -1;
    foreach (T elementToTest in actual)
    {
        try
        {
            index++;
            Func<T, bool> elementInspectorToRemove = null;
            foreach (Func<T, bool> elementInspector in allInspectors)
            {
                if (elementInspector.Invoke(elementToTest))
                {
                    elementInspectorToRemove = elementInspector;
                    break;
                }
            }

            if (elementInspectorToRemove != null)
                allInspectors.Remove(elementInspectorToRemove);
            else
                throw new CollectionException(collectionToTest, expectedLength, actualLength, index);
        }
        catch (Exception ex)
        {
            throw new CollectionException(collectionToTest, expectedLength, actualLength, index, ex);
        }
    }
}
}

The difference here is that for a collection

 string[] collectionToTest = { "Bob", "Kate" };

Both the following lines will not produce a CollectionException

 TestExpect.CollectionContainsOnlyExpectedElements(collectionToTest, x => x.Equals("Bob"), x => x.Equals("Kate"));
 TestExpect.CollectionContainsOnlyExpectedElements(collectionToTest, x => x.Equals("Kate"), x => x.Equals("Bob"));

Whereas using Assert.Collection - Only the first of the above two lines will work as the collection of inspectors is evaluated in order.

There is a potential performance impact using this method, but if you are only testing fairly small sized collections (as you probably will be in a unit test), you will never notice the difference.

Optimistic answered 29/1, 2019 at 10:59 Comment(3)
You're a BOSS. But this doesn't let you collections that aren't dictionaries - which was my problem. For example class TestData { public string FirstName {get;set;} } and Assert.Collection(new List<TestData>() { new TestData() { FirstName = "Bob" } }, item => Assert.Contains("Bob", item.FirstName); doesn't compileTurmeric
(because it's not a dictionary, it's a list)Turmeric
Hi @JohnZabroski - If I've understood your comment correctly, you should still be able to use my method like this... TestExpect.CollectionContainsOnlyExpectedElements(new List<TestData>() { new TestData() {FirstName = "Bob"}}, x => "Bob".Equals(x.FirstName)); instead of Assert.Collection.....Optimistic
T
4

Use Assert.Contains(result, item => item.Name == "John");

Tribrach answered 11/2, 2021 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.