Модульное тестирование ASP.NET приложений с использованием Moles и Pex

четверг, 26 августа 2010, Александр Краковецкий

Вступление

В этом руководстве описывается процесс написания изолированных модульных тестов для ASP.NET приложений с использованием:

  • Microsoft Moles 2010, который может быть установлен вместе с Microsoft Pex или в виде отдельного компонента (надстройка над Visual Studio 2010).
  • Microsoft Pex 2010, который автоматически создает тесты с большим процентом покрытия кода (code coverage). Microsoft Pex - это надстройка над Visual Studio для тестирования .NET приложений.

Для работы вам необходимо:

Тестирование ASP.NET приложений

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

Тестирование ASP.NET приложений может стать сложной задачей по таким причинам:

  • нельзя исполнять внутренние ASP.NET типы, такие как HttpContext, HttpRequest, HttpResponse, HttpRuntime без использования реального IIS сервера;
  • нет возможности создавать фейковые реализации таких классов, как HttpContext и HttpRuntime, так как эти классы помечены как неизменяемые (sealed) и не имеют публичных конструкторов.

Для решения этих проблем Microsoft разработала набор абстракций, такие как HttpContextBase, HttpRequestBase и т.д., которые позволяют писать тестируемые компоненты. Они расположены в сборке System.Web.Abstractions.dll для .NET 3.5 и в System.Web.dll для .NET 4.0. Это руководство акцентирует внимание на нетестируемые ASP.NET типы, которые широко используются в реальных приложениях.

Модульное тестирование ASP.NET приложений: Pex и Moles

Процесс тестирования включает:

  • Microsoft Moles — фреймворк для тестирования, который позволяет изолировать .NET код путем замены любого методов на собственный делегат, минуя жестко запрограммированные зависимости в коде.
  • Behaviors для Asp.NET — библиотека, которая перенаправляет вызовы ASP.NET API на "in-memory" модель.
  • Microsoft Pex — инструмент для автоматического тестирования, который анализирует все пути выполнения .NET кода, определяет потенциальные проблемы и автоматически генерирует набор тестов, которые покрывают большинство сценариев.

Microsoft Moles и Pex позволяют избежать некоторых сложностей при тестировании ASP.NET приложений, поэтому вы можете уделить тестированию больше внимания.

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

Требования для начала работы

Для того, что начать работу с Moles и Pex, вы должны быть знакомы с:

  • Microsoft Visual Studio 2010
  • языком C#
  • .NET Framework
  • базовыми знаниями по разработке, отладке и тестированию программного обеспечения

Конфигурация рабочей машины:

  • Windows Server 2008 R2, Windows Vista или Windows 7.
  • Visual Studio 2010 Professional (Microsoft Pex и Microsoft Moles также рабатают в Visual Studio 2008 Professional и других редакций, которые поддерживают Visual Studio Unit Testing framework).
  • Microsoft Moles 2010 или Microsoft Pex 2010, который включает фреймворк Mole.

Начинаем работу

Код, который мы будем тестировать, выглядит следующим образом:

public void RenderTitle() 
{ 
    if(this.User.IsInRole("friends")) 
    { 
        string title = (string)HttpContext.Current.Cache["data"]; 
        if(title == null) 
            this.Cache["data"] = title = "Welcome friend"; 
        this.Title = title; 
    } 
    else 
    {
       this.Title = "Welcome stranger"; 
    }
}

Метод RenderTitle является классическим примером случая, когда использование не-абстрактных классов вызывает сложности при тестировании. Использование Page.User и HttpContext.Current не позволяет тестировать код без использования IIS сервера. Значение свойства IsInRole определяется с помощью механизма аутентификации ASP.NET и не может быть легко изменен в коде. Кроме того, метод использует возможности кэширования, для которого наличие IIS сервера - обязательное требование. Кроме того, кэш вызывается как с помощью свойства HttpContext.Cache, так и с помощью свойства Page.Cache.

Шаг 1. Создание исходного и тестового приложений

В Visual studio создаем новый проект и в файле Default.aspx.cs прописываем нашу функцию в методе Page_Load. При первом запуске мы должны увидеть заголовк "Welcome stranger".

Дальше добавляем новый проект (Test Project), выбираем только что созданное приложение в качестве тестируемого, добавляем ссылку на сборку System.Web, переименовуем файл UnitTest1.cs на DefaultTest.cs и вместо сгенерированного кода пишем:

[TestClass] 
public class DefaultTest 
{ 
    [TestMethod] 
    public void RenderTitle() 
    { 
        _Default page = new _Default(); 
        page.RenderTitle(); 
    } 
}

После запуска тестового приложения вы должны получить NullReferenceException в строчке this.User.IsInRole так как User инициализируется только когда приложение работает под управлением IIS.

Шаг 2. Добавление Behaviors для изоляции модульного теста

Итак, мы столкнулись с такими проблемами при тестировании:

  • this.User - только для чтения, поэтому мы не можем изменять его в целях тестирования.
  • this.User по умолчанию - null.
  • HttpContext.Current может выполняться только под управлением IIS.
  • Cache требует наличия IIS для правильной работы.

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

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

Следующий код устанавливает роль пользователя:

using System.Web.Behaviors; 
using System.Security.Principal.Behaviors;
… 
BHttpContext context = BHttpContext.SetCurrent(); 
BIPrincipal user = context.SetUser(); 
user.Roles.SetOne("friends");

Код выше демонстрирует поведенческие типы, которые:

  • названы в честь типов, которые они моделируют и расположены в пространстве имен System.Web.Behaviors для типов из пространства System.Web. Поведенческие типы начинаются с буквы "B"
  • могут быть преобразованы к реальным типам. Например, BHttpContext легко преобразуется в HttpContext
  • реализованы на основе поведенческих коллекций фреймворка Moles. Эти коллекции определяют новый набор операторов для установки состояния, например SetAll или SetOne. Например, Roles - это BehavedSet
  • используют Moles для перенаправления вызовов ASP.NET API к их фейковым состояниям

Шаг 2.1. Подготовка тестового проекта

Необходимо добавить поведенческие типы для mscorlib.dll и System.Web.dll. Для этого в References нажимаем правой кнопкой на нужной сборке и выбираем в контекстном меню "Add Moles Assembly":

Также добавим пользовательские типы для mscorlib.dll (правой кнопкой на вкладке References):

В резульатате выполненных действий в проект будут добавлены сборки Microsoft.Moles.Framework, mscorlib.Moles, mscorlib.Behaviors, System.Web.Moles и System.Web.Behaviors:

Шаг 2.2. Замена HttpContext.Current

Обновим тестовый метод таким образом:

using System.Web.Behaviors; 

[TestClass] 
public class DefaultTest 
{ 
    [TestMethod] 
    public void RenderTitle() 
    { 
        // Arrange 
        BHttpContext context = BHttpContext.SetCurrent(); 
        _Default page = new _Default(); 
 
        // Act 
         page.RenderTitle(); 
    } 
}

Нужно добавить строку "using System.Web.Behaviors", после чего запускаем тестовый проект - в результате увидим, что тест не прошел:

В View Test Results Details смотрим, что случилось - Moles сообщает, что процесс "не измеряемый". Чтобы этого избежать, необходиом добавить атрибут HostType к нашем методу:

[TestMethod] 
[HostType("Moles")] 
public void RenderTitle()

После повторного рапуска получаем BehaviorMissingValueException при вызове IsInRole метода RenderTitle. Нажав на Default.aspx.cs можно убедиться, что ошибка возникает в строке

if(this.User.IsInRole("friends"))

BehaviorMissingValueException возникает, когда код вызывает неустановленное состояние.

Шаг 2.3. Установка ролей

В тестовом методе в секции Arrange добавим строчку, которая устанавливает роль для пользователя:

using System.Security.Principal.Behaviors; 

… 
[TestMethod] 
[HostType("Moles")] 
public void RenderTitle() 
{ 
    // Arrange 
    // set the current context 
    BHttpContext context = BHttpContext.SetCurrent(); 
    // set the current user 
    BIPrincipal user = context.SetUser(); 
    // user belongs to one role, friends 
    user.Roles.SetOne("friends"); 
    _Default page = new _Default();
}

Запускаем тест еще раз и видим, что тест опять проваливается, на этот раз в месте:

string title = (string)HttpContext.Current.Cache["data"];

Добавим следующий код для установки кэша:

BCache cache = context.SetCache(); 
cache.Items.SetEmpty();

Запускаем - и опять видим, что ошибка возникает в

this.Cache["data"] = title = "Welcome friend";

Для решения этой проблемы, необходимо "обернуть" класс Page в BPage:

_Default page = new _Default(); 
BPage basePage = new BPage(page);

Для работы BPage необходимо добавить пространство имен System.Web.UI.Behaviors.

После этого наш тест успешно пройден!

Таким образом, с помощью Moles можно тестировать объекты, которые нельзя протестировать без использования IIS сервера, что дает возможность писать модульные тесты без особых проблем. В следующий раз остановимся на использовании Pex.

P.S. В присоединенном файле документ, который описывает работу с Pex и Moles.

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии