Литмир - Электронная Библиотека

  public class HomeControllerTest

  {

    [Test]

    public void Index()

    {

      HomeController controller = new HomeController();

      ViewResult result = controller.Index() as ViewResult;

      ViewDataDictionary viewData = result.ViewData;

      Assert.AreEqual("Welcome to ASP.NET MVC!",

      viewData["Message"]);

    }

    [Test]

    public void About()

    {

      HomeController controller = new HomeController();

      ViewResult result = controller.About() as ViewResult; Assert.IsNotNull(result);

    }

  }

}

Тесты представляют собой методы, помеченные атрибутом Test, помещенные в классы, помеченные атрибутом TestFixture. Среда выполнения тестов NUnit находит в сборке с тестами тестирующие методы именно по этим атрибутам.

Структура каждого теста может быть разбита на три основных элемента:

□ подготовка данных и объектов для проведения теста;

□ выполнение действия, результат которого нужно протестировать;

□ проверка результатов выполнения действия.

Рассмотрим тестовый метод Index из листинга 8.1, проверяющий одноименное действие контроллера Home.

Подготовка данных и объектов в этом случае представляет собой только лишь создание экземпляра класса HomeController.

HomeController controller = new HomeController();

Выполнение действия — вызов метода Index у созданного ранее объекта controller, возвращающего результат типа viewResult.

ViewResult result = controller.Index() as ViewResult;

Проверка результата в данном случае заключается в проверке значения, помещенного в коллекцию ViewData с ключом Message с помощью метода класса Assert, используемого для целей отладки и тестирования.

Assert.AreEqual("Welcome to ASP.NET MVC!", viewData["Message"]);

После того как тесты созданы, сборку, содержащую тесты, нужно скомпилировать и загрузить в графическую оболочку NUnit. Оболочка распознает методы, помеченные атрибутами Test, и выводит их в виде дерева тестов, как это показано на рис. 8.3.

Тесты можно выполнять по одному, либо блоками, относящимися к одному классу, содержащему тестирующие методы, либо все тесты сразу (рис. 8.4).

Теперь представим себе, что вследствие развития проекта результат выполнения метода Index изменился, и в коллекцию ViewData с ключом Message помещается другая строка.

public ActionResult Index()

{

  ViewData["Message"] = "Всем привет!";

  return View();

}

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

Asp.net mvc framework - img_69

Asp.net mvc framework - img_70

отображено сообщение об ошибке, включающее детальную информацию, а также фрагмент исходного кода теста, сообщивший о невыполнении условий теста (рис. 8.5).

Asp.net mvc framework - img_71

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

Несколько слов о важности тестирования

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

Примечание

Особенно важно автоматическое тестирование тех действий контроллеров, которые возвращают данные для последующей обработки в JavaScript-коде на стороне клиента — отладка ошибок, связанных с некорректными данными на стороне клиента, существенно усложняется за счет введения сущности клиента и клиент-серверной отладки и зачастую не может быть эффективно автоматизирована.

*****************************

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

Тесты и MVC Framework

После прочтения предыдущей части главы можно было убедиться, что создание теста логики веб-приложения, построенного на базе MVC Framework, — задача простая. Этот эффект достигается за счет того, что ядро веб-приложения, состоящее из контроллеров и слоя работы с данными (моделей), может быть выделено в независимую от среды сборку и тестироваться вне контекста веб-сервера и без генерации разметки представления. Даже в тех случаях, когда среде выполнения необходимо взаимодействие с объектами, отвечающими за ввод/вывод разметки и запросов, передаваемых между пользователем и сервером, используются абстрактные классы (например, HttpRequestBase), реализация которых может быть подменена в тестовой среде.

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

Для поддержки возможности замены конкретной реализации необходимо использовать интерфейсы для основных зависимостей действий контроллеров. Например, если в приложении есть выделенный объект, отвечающий за восстановление и сохранение объектов данных DataManager, то в контроллерах следует использовать интерфейс iDataManager, который реализует конкретный класс DataManager. Таким образом, во время проведения тестов объект DataManager может быть подменен объектом DataManagerMock, не содержащим полного функционала работы с данными и, возможно, даже не работающим с базой данных. Этот распространенный подход к тестированию называется имитацией (англ. moking).

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

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

64
{"b":"971383","o":1}