5

In my C# unit tests, I often query for a list of rows based on a list of IDs. I then want to ensure that 1) for all the IDs, there was at least one row found that has that ID and 2) for all the returned rows, each row has an ID that is in the list of IDs to find. Here is how I usually ensure that:

Assert.IsTrue(ids.All(
    id => results.Any(result => result[primaryKey].Equals(id))
), "Not all IDs were found in returned results");

Assert.IsTrue(results.All(
    result => ids.Any(id => result[primaryKey].Equals(id))
), "Returned results had unexpected IDs");

I think the use of Any and All is convenient for such checks, but I wanted to see if anyone thinks this is less readable than it could be, or if there is perhaps a nicer way of doing two-way checks like this. I'm using MSTest in Visual Studio 2008 Team System for unit testing. This perhaps should be community wiki if it's too subjective.

Edit: I'm now using a solution based on Aviad P.'s suggestion, and also the fact that the following test passes:

string[] ids1 = { "a", "b", "c" };
string[] ids2 = { "b", "c", "d", "e" };
string[] ids3 = { "c", "a", "b" };
Assert.AreEqual(
    1,
    ids1.Except(ids2).Count()
);
Assert.AreEqual(
    2,
    ids2.Except(ids1).Count()
);
Assert.AreEqual(
    0,
    ids1.Except(ids3).Count()
);
Sarah Vessels
  • 30,930
  • 33
  • 155
  • 222

4 Answers4

4

You might choose to use the Except operator:

var resultIds = results.Select(x => x[primaryKey]);

Assert.IsTrue(resultIds.Except(ids).Count() == 0,
 "Returned results had unexpected IDs");

Assert.IsTrue(ids.Except(resultIds).Count() == 0,
 "Not all IDs were found in returned results");
Aviad P.
  • 32,036
  • 14
  • 103
  • 124
  • This looks good, and I like how I would write `x[primaryKey]` only once. However, I think it should be `Count() == 0`, considering how `Except` is described. – Sarah Vessels Jan 04 '10 at 20:09
  • Actually, you need to keep the >0, but swap the messages around. Modified my answer. – Aviad P. Jan 04 '10 at 20:10
  • Why would you need `>0`? I expect there to be no difference between the list of result IDs retrieved and the list of IDs that I queried. Intellisense describes `Except` as producing the "set difference of two sequences". – Sarah Vessels Jan 04 '10 at 20:18
  • 2
    A better alternative to `Count() == 0` is `Any()`. – Mark Seemann Jan 05 '10 at 08:06
  • Instead of doing `Count() == 0` or `Any()`, I ended up using `Assert.AreEqual` with `Count()` and `0`. – Sarah Vessels Jan 20 '10 at 20:51
3

IMO, not as readable as it could be. Create and document a method which returns true / false. Then call Assert.IsTrue(methodWithDescriptiveNameWhichReturnsTrueOrfalse(), "reason for failure");

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147
  • 2
    +1 This is essentially the *Custom Assertion* xUnit Test Pattern, although you might as well make it a void method and move the assertion into the method. – Mark Seemann Jan 04 '10 at 20:13
1

Here is a snippet of code I made to deal with two enumerables, and throwing exception while doing Unit test In MS Test, it might help :

Use

compare two enumerables:

 MyAssert.AreEnumerableSame(expected,actual);

manage exception

MyAssert.Throws<KeyNotFoundException>(() => repository.GetById(1), string.Empty);

Code

public class MyAssert
    {
        public class AssertAnswer
        {
            public bool Success { get; set; }
            public string Message { get; set; }
        }

        public static void Throws<T>(Action action, string expectedMessage) where T : Exception
        {
            AssertAnswer answer = AssertAction<T>(action, expectedMessage);

            Assert.IsTrue(answer.Success);
            Assert.AreEqual(expectedMessage, answer.Message);
        }

        public static void AreEnumerableSame(IEnumerable<object> enumerable1, IEnumerable<object> enumerable2)
        {
            bool isSameEnumerable = true;
            bool isSameObject ;

            if (enumerable1.Count() == enumerable2.Count())
            {
                foreach (object o1 in enumerable1)
                {
                    isSameObject = false;
                    foreach (object o2 in enumerable2)
                    {
                        if (o2.Equals(o1))
                        {
                            isSameObject = true;
                            break;
                        }
                    }
                    if (!isSameObject)
                    {
                        isSameEnumerable = false;
                        break;
                    }
                }
            }
            else
                isSameEnumerable = false;

            Assert.IsTrue(isSameEnumerable);
        }

        public static AssertAnswer AssertAction<T>(Action action, string expectedMessage) where T : Exception
        {
            AssertAnswer answer = new AssertAnswer();

            try
            {
                action.Invoke();

                answer.Success = false;
                answer.Message = string.Format("Exception of type {0} should be thrown.", typeof(T));
            }
            catch (T exc)
            {
                answer.Success = true;
                answer.Message = expectedMessage;
            }
            catch (Exception e)
            {
                answer.Success = false;
                answer.Message = string.Format("A different Exception was thrown {0}.", e.GetType());
            }

            return answer;
        }
    }
Arthis
  • 2,283
  • 21
  • 32
0

NUnit has the CollectionAssert family of assertions which help readability.

Peter Seale
  • 4,835
  • 4
  • 37
  • 45