Как Assert.AreEqual определяет равенство между двумя общими IEnumerables? - PullRequest
35 голосов
/ 01 июня 2009

У меня есть модульный тест, чтобы проверить, возвращает ли метод правильный IEnumerable. Метод строит перечислимое значение, используя yield return. Класс, из которого он перечисляется, ниже:

enum TokenType
{
    NUMBER,
    COMMAND,
    ARITHMETIC,
}

internal class Token
{
    public TokenType type { get; set; }
    public string text { get; set; }
    public static bool operator == (Token lh, Token rh) { return (lh.type == rh.type) && (lh.text == rh.text); }
    public static bool operator != (Token lh, Token rh) { return !(lh == rh); }
    public override int GetHashCode()
    {
        return text.GetHashCode() % type.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        return this == (Token)obj;
    }
}

Это релевантная часть метода:

 foreach (var lookup in REGEX_MAPPING)
 {
     if (lookup.re.IsMatch(s))
     {
         yield return new Token { type = lookup.type, text = s };
         break;
     }
 }

Если я сохраню результат этого метода в actual, создам еще один перечислимый expected и сравню их вот так ...

  Assert.AreEqual(expected, actual);

... утверждение не выполняется.

Я написал метод расширения для IEnumerable, который похож на Функция zip Python (она объединяет два IEnumerable в набор пар) и попробовал это:

foreach(Token[] t in expected.zip(actual))
{
    Assert.AreEqual(t[0], t[1]);
}

Это сработало! Так в чем же разница между этими двумя Assert.AreEqual с?

Ответы [ 4 ]

88 голосов
/ 01 июня 2009

Нашли:

Assert.IsTrue(expected.SequenceEqual(actual));
48 голосов
/ 01 июня 2009

Рассматривали ли вы вместо этого класс CollectionAssert ... учитывая, что он предназначен для проверки равенства коллекций?

Добавление:
Если сравниваемые «коллекции» являются перечислениями, то просто сопоставить их с «new List<T>(enumeration)» - самый простой способ выполнить сравнение. Конечно, создание нового списка приводит к некоторым накладным расходам, но в контексте модульного теста это не должно иметь большого значения, я надеюсь?

25 голосов
/ 01 июня 2009

Assert.AreEqual собирается сравнить два объекта под рукой. IEnumerable являются типами сами по себе и предоставляют механизм для перебора некоторой коллекции ... но на самом деле они не являются этой коллекцией. Ваше оригинальное сравнение сравнивало два IEnumerable с, что является допустимым сравнением ... но не тем, что вам нужно. Вам нужно было сравнить с тем, что два IEnumerable были предназначены для перечисления .

Вот как я сравниваю два перечислимых значения:

Assert.AreEqual(t1.Count(), t2.Count());

IEnumerator<Token> e1 = t1.GetEnumerator();
IEnumerator<Token> e2 = t2.GetEnumerator();

while (e1.MoveNext() && e2.MoveNext())
{
    Assert.AreEqual(e1.Current, e2.Current);
}

Я не уверен, что приведенный выше код меньше, чем ваш .Zip метод, но он настолько же прост, как и получается.

19 голосов
/ 18 июня 2012

Я думаю, что самый простой и ясный способ заявить о желаемом равенстве - это сочетание ответа jerryjvl и комментария к его сообщению MEMark - объедините CollectionAssert.AreEqual с методами расширения:

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());

Это дает более подробную информацию об ошибке, чем ответ SequenceEqual, предложенный OP (он сообщит вам, какой элемент был найден, который был неожиданным). Например:

IEnumerable<string> expected = new List<string> { "a", "b" };
IEnumerable<string> actual   = new List<string> { "a", "c" }; // mismatching second element

CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray());
// Helpful failure message!
//  CollectionAssert.AreEqual failed. (Element at index 1 do not match.)    

Assert.IsTrue(expected.SequenceEqual(actual));
// Mediocre failure message:
//  Assert.IsTrue failed.   

Вы будете действительно довольны тем, что сделали это таким образом, если / когда ваш тест не пройден - иногда вы даже можете узнать, в чем дело, не вызывая отладчик - и эй, вы правильно делаете TDD Итак, вы сначала пишете провальный тест, верно? ; -)

Сообщения об ошибках становятся еще более полезными, если вы используете AreEquivalent для проверки на эквивалентность (порядок не имеет значения):

CollectionAssert.AreEquivalent(expected.ToList(), actual.ToList());
// really helpful error message!
//  CollectionAssert.AreEquivalent failed. The expected collection contains 1
//  occurrence(s) of <b>. The actual collection contains 0 occurrence(s).   
...