简体   繁体   中英

Compare two List<string[]> objects in C# Unit Test

I'm trying to create a Unit Test that compares two lists of string arrays.

I tried creating two of the exact same List<string[]> objects, but when I use CollectionAssert.AreEqual(expected, actual); , the test fails:

[TestMethod]
public void TestList()
{
    List<string[]> expected = new List<string[]> {
        new string[] { "John", "Smith", "200" },
        new string[] { "John", "Doe", "-100" }
    };

    List<string[]> actual = new List<string[]> {
        new string[] { "John", "Smith", "200" },
        new string[] { "John", "Doe", "-100" }
    };

    CollectionAssert.AreEqual(expected, actual);
}

I've also tried Assert.IsTrue(expected.SequenceEqual(actual)); , but that fails as well.

Both these methods work if I am comparing two Lists of strings or two arrays of strings, but they do not work when comparing two Lists of arrays of strings.

I'm assuming these methods are failing because they are comparing two Lists of object references instead of the array string values.

How can I compare the two List<string[]> objects and tell if they are really the same?

CollectionAssert.AreEqual(expected, actual); fails, because it compares object references. expected and actual refer to different objects.

Assert.IsTrue(expected.SequenceEqual(actual)); fails for the same reason. This time the contents of expected and actual are compared, but the elements are themselves different array references.

Maybe try to flatten both sequences using SelectMany :

var expectedSequence = expected.SelectMany(x => x).ToList();
var actualSequence = actual.SelectMany(x => x).ToList();
CollectionAssert.AreEqual(expectedSequence, actualSequence);

As Enigmativity correctly noticed in his comment, SelectMany may give a positive result when the number of arrays and/or their elements are different, but flattening the lists will result in an equal number of elements. It is safe only in the case when you always have the same number of arrays and elements in these arrays.

It is failing because the items in your list are objects ( string[] ) and since you did not specify how CollectionAssert.AreEqual should compare the elements in the two sequences it is falling back to the default behavior which is to compare references. If you were to change your lists to the following, for example, you would find that the test passes because now both lists are referencing the same arrays:

var first = new string[] { "John", "Smith", "200" };
var second = new string[] { "John", "Smith", "200" };

List<string[]> expected = new List<string[]> { first, second};
List<string[]> actual = new List<string[]> { first, second};

To avoid referential comparisons you need to tell CollectionAssert.AreEqual how to compare the elements, you can do that by passing in an IComparer when you call it:

CollectionAssert.AreEqual(expected, actual, StructuralComparisons.StructuralComparer);

The best solution would be to check both the items in each sub-collection as well as the number of items in each respective sub-collection.

Try with this:

bool equals = expected.Count == actual.Count &&
              Enumerable.Range(0, expected.Count).All(i => expected[i].Length == actual[i].Length &&
                                                           expected[i].SequenceEqual(actual[i]));
Assert.IsTrue(equals);

This will check that:

  • Both the lists have the same length
  • All the pair of sub-collections in both lists have the same length
  • The items in each pair of sub-collections are the same

Note : using SelectMany isn't a good idea since it could create a false positive you have the same items in your second list, but spread out in different sub-collections. I mean, it'd consider two lists to be the same even if the second one had the same items all in a single sub-collection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM