Основы модульного (Unit) тестирования в Visual Studio

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

В качестве одного из вариантов решения данной задачи можно рассматривать модульное или Unit тестирование.

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

При этом «тестовый» компонент можно запускать на выполнение, не компилируя программу целиком, в том числе в автоматическом режиме. Это обеспечивает достаточно высокую степень автоматизации процесса тестирования и значительно сокращает время на его выполнение.

Важно отметить, что задача автоматизации тестирования в принципе не может быть решена полностью. В частности невозможно автоматизировать исследовательское тестирование [1]. Однако автоматизировать рутинные операции, например, интеграционное и регрессионное тестирование можно вполне. Последнее особенно важно, так как при создании новой версии программного обеспечения значительный объём работ по тестированию состоит именно в том, чтобы убедиться, что новый функционал не привёл к ошибкам в работе уже существующего.

Что собой представляет модульный (Unit) тест

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

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

Подобные фреймворки часто входят в состав интегрированных сред разработки (IDE). Собственный фреймворк для модульных тестов имеет и Visual Studio.

Для его использования в разделе «Тест» окна создания нового проекта есть специальный шаблон под названием «Проект модульного теста».

Что собой представляет данный шаблон?

При создании проекта модульного теста создаётся обычный класс, но:

  • Как сам класс, так и его методы помечаются специальными атрибутами TestClass и TestMethod соответственно.
    Данные атрибуты сообщают компилятору о том, что это класс модульного теста и тестовые методы.
  • Методы класса должны быть открытыми (public) и иметь тип void.

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

Тестовые методы предназначены для реализации непосредственно процесса тестирования. Для проведения тестирования класс модульного теста должен включать как минимум один тестовый метод.

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

Тест считается не пройденным (в работе программы присутствует ошибка), если в ходе выполнения тестового метода возникло исключение.

Ниже представлена «шаблонная» структура класса модульного теста. Обратите внимание на подключение пространства имён Microsoft.VisualStudio.TestTools.UnitTesting.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MyUnitTestProject
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

Работа модульного тестирования в Visual Studio будет показана далее.

Простейший пример модульного (Unit) тестирования

В качестве примера для наглядности и лучшего понимания возьмём простейший класс.

public class MyClass
{
    public int Method1(int a, int b)
    {
        return a * b;
    }
    public void Method2()
    {
        throw new Exception();
    }
}

Данный класс содержит только два метода.

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

Второй имитирует ошибку, заложенную в программе в процессе написания.

Создадим модульный тест для этого класса.

Для этого добавим в имеющееся решение проект модульного теста, в котором, в свою очередь добавим ссылку на сборку, которая содержит тестируемый класс.

Пусть первоначально тест будет включать также два метода. Тестовые случаи также будут предельно просты. Создаётся экземпляр тестируемого класса и вызывается его соответствующий метод.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MyWindowsFormsApplication;
namespace MyUnitTestProject
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            MyClass myClass = new MyClass();
            myClass.Method1(3, 4);
        }
        [TestMethod]
        public void TestMethod2()
        {
            MyClass myClass = new MyClass();
            myClass.Method2();
        }
    }
}

Запустим тест на выполнение. Для этого в главном меню Visual Studio выберем «Тест» — «Выполнить» — «Все тесты».

На экране появится панель «Обозреватель тестов», в которой спустя некоторое время будут отображены результаты тестирования.

В данном случае тестирование завершилось неудачно, так как один из методов тестируемого класса содержал ошибку (имитируемую).

Если изменить этот метод, устранив ошибку. Например, так:

public void Method2()
{
    return;
}

Тестирование будет завершено успешно.

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

Усложним задачу.

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

[TestMethod]
public void TestMethod3()
{
    MyClass myClass = new MyClass();
    if (myClass.Method1(3, 4) != 12)
    {
        throw new Exception();
    };
}

Первоначально все тесты проходят успешно.

Теперь внесём в тестируемый метод ошибку. Причём это будет ошибка, которая сама по себе не вызывает исключения.

Например:

public int Method1(int a, int b)
{
    return a * b * 10;
}

Таким образом, при тех же самых исходных данных результат будет уже не 12, а 120.

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

Как уже говорилось в самом начале, всё это лишь самые простейшие примеры.

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

Достоинства и недостатки модульного (Unit) тестирования

В завершение хотелось бы сказать несколько слов о «плюсах» и «минусах» модульных тестов.

Достоинства

  • Модульные тесты значительно оптимизируют сам процесс тестирования.
    Тестовые случаи обрабатываются в автоматическом режиме. Тестировщику остаётся только анализировать результат.
  • Более высокий уровень контроля качества по сравнению с «обычным» ручным тестированием.
    При автоматизированном тестировании практически исключается возможность пропуска тех или иных тестов.
    Кроме того решается проблема с описанием тестовых случаев. При написании теста они естественным образом документируются в его коде.

Недостатки

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

  • Модульные тесты это программные сценарии.
    То есть, данные тесты сами являются программами и, как следствие, не защищены от возможных ошибок свойственных программам.
  • Написание модульных тестов требует времени.
    Наиболее оптимальным считается покрытие тестами 70-80% кода [1] (для 70-80% кода программы должны быть написаны модульные тесты).
    Таким образом при разработке нового программного обеспечения или его компонентов, затраты времени на написание тестов становятся сопоставимы с самим процессом разработки. Экономия времени проявляется только при регрессионном и интеграционном тестировании.
    Данное обстоятельство неизбежно сказывается на сроках выполнения проекта.
  • Модульные тесты не способны полностью заменить ручное тестирование, и тем более исключить необходимость владения теорией и технологиями тестирования.
    О первом уже немного говорилось в самом начале статьи. Модульные тесты не в состоянии заменить такие виды тестирования как, например, исследовательское или тестирование юзабилити.
    Кроме того, если внимательно присмотреться даже к тем элементарным примерам, которые приведены выше, в них можно увидеть реализацию простейших функциональных тестов. Единственная особенность – эти тесты записаны в виде программного сценария, и выполняться они будут не человеком, а Visual Studio.
    Поэтому, также как и при ручном тестировании, для правильно написания модульных тестов владение соответствующими знаниями и навыками в области тестирования обязательны.
  • Применение модульного тестирования требует не только определённой квалификации, но и высокой культуры разработки.
    Модульные тесты на самом деле довольно тонкий и сложный инструмент. При неправильном использовании, они могут не только оказаться бесполезны, но и навредить процессу разработки.
    При этом, как показывает практика, зачастую даже специальные рекомендации для подобных случаев (например, из [2]) не в силах исправить ситуацию.
    Не стоит надеяться на то, что модульные тесты сами по себе улучшат качество готового программного продукта и решат вопросы, связанные с организационными и кадровыми издержками.
    Это не панацея, а инструмент в помощь разработчикам. Не более.

Модульные тесты при правильном применении способны принести большую пользу разработчикам, которая нивелирует указанные недостатки.

Хочется надеяться, что данная статья будет полезна не только для ознакомления с модульным тестированием средствами Visual Studio, но и станет первым шагом при освоении и внедрении данных технологий в профессиональной деятельности.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *