Страницы

понедельник, 13 февраля 2012 г.

Эффективное тестирование. Инструменты.

  Каждый мастер должен иметь в своём арсенале надёжные и удобные инструменты. И для тестирования .NET приложений инструментов много MSTest, NUnit, MbUnit, XUnit и даже свой DSL для написания тестов – T#. Список не полный, но думаю понять объёмы популярности тестирования достаточно. Я не собираюсь устраивать сравнения между ними, слишком много для этого надо написать. Но для начала я бы рекомендовал NUnit. Он наиболее популярен в .NET мире, и наиболее прост для начинающего (опытные смогут выбрать сами Улыбка).  Просто скачайте установщик с официального сайта, подключите к проекту ссылку на nunit.framework.dll. Всё готово для первого  теста. Допустим, мы делаем класс, реализующий функционал работы с версиями. У класса  есть четыре целочисленных свойства Major, Minor, Build и Revision. Конструктор класса должен принимать разделённую точками строку, как это принято в AssemblyVersionAttribute. Тогда тест может выглядеть вот так:
[TestFixture]
public class VersionFixture
{
    [Test]
    public void Constructor_parses_string_properly()
    {
        var version = new Version("1.2.330.400");

        //Обратите внимание, все сообщения консоли будут выведены в GUI NUnit
        //Часто таким способом полезно "логгировать" прохождене теста
        Console.WriteLine("Major={0} Minor={1} Build={2} Revision = {3}", version.Major, version.Minor, version.Build, version.Revision);

        Assert.AreEqual(1, version.Major);
        Assert.AreEqual(2, version.Minor);
        Assert.AreEqual(330, version.Build);
        Assert.AreEqual(400, version.Revision);
    }
}

  Скомпилируйте проект, запустите NUnit,  откройте скомпилированную dll с тестами, и жмите кнопку Run. В итоге должны увидеть нечто подобное:

image



  Ошибочка, тест не прошёл. NUnit показывает, на какой строке произошла ошибка. Видимо я ошибся в “парсере” строки. Ладно, поправим код и попробуем запустить еще раз:

image

  Так гораздо лучше! Я нашёл ошибку в процессе подготовки кода примеров для этого сообщения. Улыбка

  Тесты выше можно переписать с помощью нового  FluentAPI NUnit:
[TestFixture]
public class VersionFixture
{
    [Test]
    public void Constructor_parses_string_properly_with_fluent_API()
    {
        var version = new Version("1.2.330.400");

        //Обратите внимание, все сообщения консоли будут выведены в GUI NUnit
        //Часто таким способом полезно "логгировать" прохождене теста
        Console.WriteLine("Major={0} Minor={1} Build={2} Revision = {3}", version.Major, version.Minor, version.Build, version.Revision);

        Assert.That(version.Major, Is.EqualTo(1));
        Assert.That(version.Minor, Is.EqualTo(2));
        Assert.That(version.Build, Is.EqualTo(330));
        Assert.That(version.Revision, Is.EqualTo(400));
    }
}
  Список ниже не полный, но содержит наиболее часто используемые атрибуты:

  • TestFixture – Используется NUnit’ом для поиска классов тестов в сборке.
  • TestFixtureSetUp – метод выполняется один раз перед всеми тестами.
  • SetUp – метод выполняется перед каждым тестом.
  • Test – обозначает метод-тест.
  • TearDown - метод выполняется после каждого теста.
  • TestFixtureTearDown - метод выполняется один раз после всех тестами.
  • Ignore – не выполнять тест. Можно помечать методы класс.
  • Explicit – тест выполнится только при явном запуске из GUI. Можно помечать методы и класс.

  NUnit создаёт один экземпляр класса, отмеченного атрибутом TestFixture, и использует его для всех тестов в этом классе. Поэтому важно перед каждым тестом инициализировать и очищать состояние класса. Это можно сделать с помощью атрибутов Setup/Teardown TestFixtureSetUp/TestFixtureTearDown.

  Иногда тест долго не удаётся сделать “зелёным”. Тогда можно временно исключить его из набора тестов с помощью атрибута Ignore. Почему просто не закомментировать? Потому что с атрибутом NUnit выдаст информативное сообщение, какие тесты временно отключены. Это хорошее напоминание – такие тесты надо как можно скорее приводить в рабочее состояние, и убирать атрибут Ignore. Explicit делает то же самое. Его применяют для демонстрации явных намерений “этот тест выполнятся, только если именно его и хотят запустить”.

  Отличное описание атрибутов и Assert-методов есть в статье про NUnit 2.5. Настойчиво рекомендую ее изучить, и периодически заглядывать. Знание возможных вариантов будет весьма полезных для выбора наиболее подходящего к конкретному случаю, и позволит существенно упростить код тестов.

  Не сводите все тесты к вызову Assert.True()/Assert.False(), например так: 
[TestFixture]
public class VersionFixture
{
    [Test]
    public void Constructor_parses_string_properly_in_the_wrong_way()
    {
        var version = new Version("1.2.330.400");

        //Обратите внимание, все сообщения консоли будут выведены в GUI NUnit
        //Часто таким способом полезно "логгировать" прохождене теста
        Console.WriteLine("Major={0} Minor={1} Build={2} Revision = {3}", version.Major, version.Minor, version.Build, version.Revision);
        
        //Так делать очень плохо, т.к.. в итоге сообщение об ошибке будет невыразительным
        Assert.IsTrue(version.Major == 1);
        Assert.IsTrue(version.Minor == 2);
        Assert.IsTrue(version.Build == 330);
        Assert.IsTrue(version.Revision == 400);
    }
}

  В этом случае будет ничего не говорящее о сути происходящего сообщение:

Expected: True
But was: False

  Используйте подходящий случаю Assert-метод (или его FluentAPI аналог). Не ленитесь, напишите несколько строк в сообщение об ошибке в методе Assert. В случае сломанного теста это сообщение сильно облегчает понимание проблемы.

  Еще один штрих – все мы любим работать в Visual Studio, и хотелось бы запускать тесты прямо из IDE. Решений много, могу лишь упомянуть Resharper, TestDriven.Net, и еще бесплатный Visual NUnit 2010. Совсем простой вариант – настроить запуск NUnit как External Tool. (Кстати, таким образом можно еще и отлаживать код тестов).

  Для вступления в автоматизированное тестирование достаточно. Код примеров здесь. Берите NUnit в руки, и смелее к тестам.  В следующий раз расскажу о нескольких более продвинутых инструментах для тестирования.

Комментариев нет:

Отправить комментарий