Модульные тесты для сравнения текстовых файлов в NUnit

У меня есть класс, который обрабатывает 2 файла xml и создает текстовый файл.

Я хотел бы написать кучу модульных/интеграционных тестов, которые могут индивидуально пройти или не пройти для этого класса, которые делают следующее:

  1. Для ввода A и B сгенерируйте вывод.
  2. Сравните содержимое сгенерированного файла с ожидаемым содержимым.
  3. Когда фактическое содержимое отличается от ожидаемого, происходит сбой и отображается некоторая полезная информация о различиях.

Ниже представлен прототип класса вместе с моей первой пробой модульных тестов.

Есть ли шаблон, который я должен использовать для такого рода тестирования, или люди склонны писать миллионы функций TestX()?

Есть ли лучший способ уговорить различия текстовых файлов с NUnit? Должен ли я внедрить алгоритм сравнения текстовых файлов?


class ReportGenerator
{
    string Generate(string inputPathA, string inputPathB)
    {
        //do stuff
    }
}

[TextFixture]
public class ReportGeneratorTests
{
     static Diff(string pathToExpectedResult, string pathToActualResult)
     {
         using (StreamReader rs1 = File.OpenText(pathToExpectedResult))
         {
             using (StreamReader rs2 = File.OpenText(pathToActualResult))
             {
                 string actualContents = rs2.ReadToEnd();
                 string expectedContents = rs1.ReadToEnd();                  

                 //this works, but the output could be a LOT more useful.
                 Assert.AreEqual(expectedContents, actualContents);
             }
         }
     }

     static TestGenerate(string pathToInputA, string pathToInputB, string pathToExpectedResult)
     {
          ReportGenerator obj = new ReportGenerator();
          string pathToResult = obj.Generate(pathToInputA, pathToInputB);
          Diff(pathToExpectedResult, pathToResult);
     }

     [Test]
     public void TestX()
     {
          TestGenerate("x1.xml", "x2.xml", "x-expected.txt");
     }

     [Test]
     public void TestY()
     {
          TestGenerate("y1.xml", "y2.xml", "y-expected.txt");
     }

     //etc...
}

Обновлять

Я не заинтересован в тестировании функциональности diff. Я просто хочу использовать его для создания более читаемых сбоев.


person Adam Tegen    schedule 26.09.2008    source источник


Ответы (5)


Что касается нескольких тестов с разными данными, используйте расширение NUnit RowTest:

using NUnit.Framework.Extensions;

[RowTest]
[Row("x1.xml", "x2.xml", "x-expected.xml")]
[Row("y1.xml", "y2.xml", "y-expected.xml")]
public void TestGenerate(string pathToInputA, string pathToInputB, string pathToExpectedResult)
 {
      ReportGenerator obj = new ReportGenerator();
      string pathToResult = obj.Generate(pathToInputA, pathToInputB);
      Diff(pathToExpectedResult, pathToResult);
 }
person Adam Tegen    schedule 26.09.2008

Вероятно, вы просите провести тестирование по «золотым» данным. Я не знаю, есть ли во всем мире специальный термин для такого рода тестирования, но мы делаем это именно так.

Создайте базовый класс приспособления. В основном он имеет «void DoTest (string fileName)», который будет считывать определенный файл в память, выполнять абстрактный метод преобразования «string Transform (string text)», затем читать fileName.gold из того же места и сравнивать преобразованный текст с ожидаемым . Если содержимое отличается, выдается исключение. Вызванное исключение содержит номер строки первой разницы, а также текст ожидаемой и фактической строк. Поскольку текст стабилен, этой информации обычно достаточно, чтобы сразу определить проблему. Обязательно пометьте строки «Ожидаемое:» и «Фактическое:», иначе вы будете вечно гадать, что есть что, глядя на результаты теста.

Затем у вас будут специальные тестовые приспособления, в которых вы реализуете метод Transform, который выполняет правильную работу, а затем будут тесты, которые выглядят следующим образом:

[Test] public void TestX() { DoTest("X"); }
[Test] public void TestY() { DoTest("Y"); }

Название неудавшегося теста мгновенно подскажет, что сломалось. Конечно, вы можете использовать тестирование строк для группировки похожих тестов. Наличие отдельных тестов также помогает в ряде ситуаций, таких как игнорирование тестов, передача тестов коллегам и так далее. Создать сниппет, который создаст тест за секунду, не составляет большого труда, вы потратите гораздо больше времени на подготовку данных.

Затем вам также понадобятся некоторые тестовые данные и способ, которым ваш базовый прибор их найдет, обязательно настройте правила для этого для проекта. Если тест не пройден, выгрузите фактический вывод в файл рядом с золотом и сотрите его, если тест пройден. Таким образом, вы можете использовать инструмент сравнения, когда это необходимо. Когда данные о золоте не найдены, тест завершается неудачно с соответствующим сообщением, но фактический вывод все равно записывается, поэтому вы можете проверить его правильность и скопировать его, чтобы он стал «золотым».

person Ilya Ryzhenkov    schedule 28.09.2008

Я бы, вероятно, написал один модульный тест, содержащий цикл. Внутри цикла я читал 2 xml-файла и diff-файл, а затем сравнивал xml-файлы (без записи на диск) и сравнивал их с diff-файлом, прочитанным с диска. Файлы будут пронумерованы, например. a1.xml, b1.xml, diff1.txt; a2.xml, b2.xml, diff2.txt; a3.xml, b3.xml, diff3.txt и т. д., и цикл останавливается, когда не удается найти следующее число.

Затем вы можете писать новые тесты, просто добавляя новые текстовые файлы.

person Alan Hensel    schedule 26.09.2008
comment
Единственная проблема в том, что у вас есть только 1 тест. Мне было бы хорошо знать, сломался ли я только 1 из 80 тестов или я сломал 70 из 80 тестов. Информация о том, какие тесты сломались, может помочь в отслеживании проблем. - person Adam Tegen; 27.09.2008
comment
Итак, это зависит от того, как вы пишете тест. Другими словами, если различия не совпадают, то Assert.Fail (ваше более информативное сообщение здесь). Или добавьте к списку неудачных тестов и Assert.Fail после завершения цикла, если список не пуст. - person Alan Hensel; 27.09.2008

Вместо вызова .AreEqual вы можете самостоятельно проанализировать два входных потока, вести подсчет строк и столбцов и сравнивать содержимое. Как только вы обнаружите разницу, вы можете сгенерировать сообщение вроде...

Строка 32, столбец 12 — найдено «x», хотя ожидалось «y»

Вы можете дополнительно улучшить это, отобразив несколько строк вывода.

Разница в строке 32, столбце 12, показана первая разница
A = это txst
B = это tests

Обратите внимание, как правило, я обычно генерирую через свой код только один из двух имеющихся у вас потоков. Другой я бы взял из тестового/текстового файла, проверив на глаз или другим методом правильность содержащихся данных!

person Ray Hayes    schedule 26.09.2008

Я бы, вероятно, использовал XmlReader для перебора файлов и их сравнения. Когда я сталкивался с разницей, я отображал XPath в место, где файлы разные.

PS: Но на самом деле мне всегда было достаточно просто прочитать весь файл в строку и сравнить две строки. Для отчета достаточно увидеть, что тест провален. Затем, когда я выполняю отладку, я обычно различаю файлы, используя Araxis Merge, чтобы увидеть, где именно У меня есть проблемы.

person David Pokluda    schedule 27.09.2008