Подтвердить что ты не робот

Как Assert.AreEqual определяет равенство между двумя универсальными IEnumerables?

У меня есть unit test, чтобы проверить, возвращает ли метод правильный 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, который похож на Python zip function (он объединяет два IEnumerables в набор пар ) и попробовал это:

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

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

4b9b3361

Ответ 1

Assert.AreEqual собирается сравнить два объекта под рукой. IEnumerable сами по себе являются типами и обеспечивают механизм для итерации над некоторой коллекцией... но они на самом деле не являются этой коллекцией. Ваше первоначальное сравнение сравнило два IEnumerable s, что является допустимым сравнением... но не то, что вам нужно. Вам нужно было сравнить то, что предназначалось для двух 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, но он примерно такой же простой, как и он.

Ответ 2

Найдено:

Assert.IsTrue(expected.SequenceEqual(actual));

Ответ 3

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

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

Ответ 4

Я думаю, что самый простой и ясный способ утвердить желаемое равенство - это комбинация ответа 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).