Как вы тестируете метод со сложным вводом-выводом - PullRequest
4 голосов
/ 14 июня 2010

Когда у вас есть простой метод, такой как, например, sum (int x, int y), легко написать модульные тесты. Вы можете проверить, что метод будет правильно суммировать два примера целых чисел, например, 2 + 3 должен вернуть 5, затем вы проверите то же самое для некоторых «необычных» чисел, например, отрицательных значений и нуля. Каждый из них должен быть отдельным модульным тестом, так как отдельный модульный тест должен содержать одно утверждение.

Что вы делаете, когда у вас сложный ввод-вывод? Взять, к примеру, XML-парсер. Вы можете иметь один метод parse (String xml), который получает String и возвращает объект Dom. Вы можете написать отдельные тесты, которые будут проверять, что определенный текстовый узел анализируется правильно, что атрибуты анализируются нормально, что дочерний узел принадлежит родительскому и т. Д. Для всех этих случаев я могу написать простой ввод, например

<root><child/></root>

, который будет использоваться для проверки родительско-дочерних отношений между узлами и т. Д. Для остальных ожиданий.

Теперь взгляните на следующий XML:

<root>
  <child1 attribute11="attribute 11 value" attribute12="attribute 12 value">Text 1</child1>
  <child2 attribute21="attribute 21 value" attribute22="attribute 22 value">Text 2</child2>
</root>

Чтобы проверить, что метод работает правильно, мне нужно проверить много сложных условий, например, что attribute11 и attribute12 принадлежат element1, что Text 1 принадлежит child1 и т. Д. Я не хочу ставить более одного утверждения в моем модульном тесте. Как мне это сделать?

Ответы [ 7 ]

4 голосов
/ 14 июня 2010

Все, что вам нужно - это проверить один аспект SUT (тестируемой системы) в отдельном тесте.

[TestFixture]
    public class XmlParserTest
    {
        [Test, ExpectedException(typeof(XmlException))]
        public void FailIfXmlIsNotWellFormed()
        {
            Parse("<doc>");
        }

        [Test]
        public void ParseShortTag()
        {
            var doc = Parse("<doc/>");

            Assert.That(doc.DocumentElement.Name, Is.EqualTo("doc"));
        }

        [Test]
        public void ParseFullTag()
        {
            var doc = Parse("<doc></doc>");

            Assert.That(doc.DocumentElement.Name, Is.EqualTo("doc"));
        }

        [Test]
        public void ParseInnerText()
        {
            var doc = Parse("<doc>Text 1</doc>");

            Assert.That(doc.DocumentElement.InnerText, Is.EqualTo("Text 1"));
        }

        [Test]
        public void AttributesAreEmptyifThereAreNoAttributes()
        {
            var doc = Parse("<doc></doc>");

            Assert.That(doc.DocumentElement.Attributes, Has.Count(0));
        }

        [Test]
        public void ParseAttribute()
        {
            var doc = Parse("<doc attribute11='attribute 11 value'></doc>");

            Assert.That(doc.DocumentElement.Attributes[0].Name, Is.EqualTo("attribute11"));
            Assert.That(doc.DocumentElement.Attributes[0].Value, Is.EqualTo("attribute 11 value"));
        }

        [Test]
        public void ChildNodesInnerTextAtFirstLevel()
        {
            var doc = Parse(@"<root>
              <child1>Text 1</child1>
              <child2>Text 2</child2>
            </root>");

            Assert.That(doc.DocumentElement.ChildNodes, Has.Count(2));
            Assert.That(doc.DocumentElement.ChildNodes[0].InnerText, Is.EqualTo("Text 1"));
            Assert.That(doc.DocumentElement.ChildNodes[1].InnerText, Is.EqualTo("Text 2"));
        }

        // More tests 
        .....

        private XmlDocument Parse(string xml)
        {
            var doc = new XmlDocument();

            doc.LoadXml(xml);

            return doc;
        }
    }

Такой подход дает множество преимуществ:

  1. Простое обнаружение дефектов - если что-то не так с синтаксическим анализом атрибутов, то только тесты по атрибутам не пройдут.
  2. Маленькие тесты всегда легче понять

UPD: посмотрите, что Джерард Месарош (автор книги тестовых шаблонов xUnit) говорит о теме: xunitpatterns

Один из возможных спорных аспектов проверки одного условия на тест - это то, что мы подразумеваем под «одним условием».Некоторые тест-драйверы настаивают на одном утверждении на тест.Эта настойчивость может основываться на использовании класса Testcase для каждой организации Fixture методов Test и именовании каждого теста на основе того, что проверяется одним утверждением (например, AwaitingApprovalFlight.validApproverRequestShouldBeApproved.).Наличие одного утверждения на тест делает такое именование очень простым, но оно приводит к большему количеству тестовых методов, если нам приходится утверждать во многих выходных поляхКонечно, мы часто можем согласиться с этой интерпретацией, извлекая пользовательское утверждение (стр. X) или метод проверки (см. Пользовательское утверждение), что позволяет нам уменьшить количество вызовов метода подтверждения в один.Иногда это делает тест более читабельным, но если нет, я бы не стал слишком настойчиво настаивать на одном утверждении.

1 голос
/ 14 июня 2010

Чтобы немного пояснить краткий ответ Яна: настройте этот бит XML и проведите отдельные индивидуальные тесты, каждый со своим утверждением. Таким образом, вы не дублируете логику установки, но у вас все еще есть детальное понимание того, что не так с вашим анализатором.

1 голос
/ 14 июня 2010

Используйте несколько тестов. Применяются те же ограничения. Вы должны проверить некоторые нормальные рабочие случаи, некоторые неудачные случаи и некоторые крайние случаи.

Точно так же, как вы можете предполагать, что если сумма (x, y) работает для некоторых значений x, она будет работать с другими значениями, вы можете предположить, что если синтаксический анализатор XML может анализировать последовательность из 2 узлов, он может также проанализировать последовательность из 100 узлов.

1 голос
/ 14 июня 2010

Несколько тестов.

0 голосов
/ 14 июня 2010

Вы также можете использовать свои собственные утверждения (это взято из вашего собственного вопроса):

attribute11 и attribute12 относятся к элементу1

('attribute11 ', 'attribute12').belongsTo('element1');

или

('element1 attribute11').length

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

0 голосов
/ 14 июня 2010

У меня было похожее требование, где я хотел иметь 1 Assert для различных входных наборов.Пожалуйста, проверьте ссылку ниже, которую я в блоге.

Написание более качественных модульных тестов

Это может быть применимо и к вашей задаче.Создайте фабричный класс, который содержит логику для построения «сложных» входных наборов.В модульном тесте есть только один Assert.

Надеюсь, это поможет.

Спасибо, Виджай.

0 голосов
/ 14 июня 2010

Использовать Nunit Fluent Syntax

Assert.That( someString,
      Is.Not.Null
      .And.Not.Empty
      .And.EqualTo("foo")
      .And.Not.EqualTo("bar")
      .And.StartsWith("f"));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...