Главная страница   /   20.3. Взаимодействие с формами (ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

Джеффри Палермо

20.3. Взаимодействие с формами

В данной книге мы тщательно избегали значимости использования строго типизированных представлений и базирующихся на выражениях вспомогательных HTML-методов. Это позволяло нам пользоваться преимуществами современных инструментов рефакторинга, которые могут обновлять код нашего представления автоматически в случае изменения названий элементов. Почему тогда мы возвращаемся к жестко-закодированным "магическим" строкам в наших тестах пользовательского интерфейса? Мы можем избежать тех же проблем, которые мы решали в рамках использования строго типизированных представлений, применяя в наших тестах пользовательского интерфейса похожие приемы для взаимодействия с формами.

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

Листинг 20-8: Строго типизированное представление, использующее шаблоны редактирования
@using UITesting.Models;
@model ProductForm
@{
	ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
	@Html.EditorForModel()
	<input type="submit" value="Save" />
}

Строка 1: Объявляет строго типизированное представление

Строка 9: Создает форму Edit

Наше представление Edit – это строго типизированное представление для типа модели представления ProductForm. Мы используем возможности шаблонов редактирования, введенных в ASP.NET MVC 2, для удаления необходимости ручного кодирования индивидуальных элементов input и label. Метод EditorForModel также позволяет изменять название любого элемента нашей ProductForm без разрушения нашего представления или действия контроллера.

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

Листинг 20-9: Использование переменного API и синтаксиса, базирующегося на выражениях, для заполнения форм
[Test]
public void Should_update_product_price_successfully()
{
	NavigateLink(LocalSiteMap.Nav.Products);
	Browser.Link(Find.ByText("Edit")).Click();
	ForForm<ProductForm>()
		.WithTextBox(form => form.Price, 389.99m)
		.Save();
	...
}

Строка 6: Использует вспомогательный метод, базирующийся на выражениях

Этот простой переменный интерфейс начинается с указания типа модели представления посредством вызова метода ForForm. Метод ForForm создает объект FluentForm, который мы вкратце рассмотрим. Затем вызов метода WithTextBox пристыковывается к результату метода ForForm и принимает в качестве параметра выражение, используемое для определения свойства для ViewModel, а также значения, которым будет заполняться элемент ввода данных. Наконец, метод Save нажимает кнопку Save на форме.

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

Листинг 20-10: Метод ForForm класса WebTestBase
protected FluentForm<TForm> ForForm<TForm>()
{
	return new FluentForm<TForm>(Browser);
}

Метод ForForm принимает единственный родовой параметр, тип формы. Этот метод возвращает объект FluentForm, в котором упакован набор вспомогательных методов, созданных для взаимодействия со строго типизированным представлением.

Метод ForForm создает экземпляр нового объекта FluentForm, передавая объект IE в конструктор FluentForm, как это показано ниже.

Листинг 20-11: Класс FluentForm и конструктор
public class FluentForm<TForm>
{
	private readonly IE _browser;
	public FluentForm(IE browser)
	{
		_browser = browser;
	}
	...
}

Конструктор FluentForm принимает объект IE (строка 4) и сохраняет его в закрытом поле (строка 6) для последующих взаимодействий.

Следующий метод, который вызывается в листинге 20-9, – это метод WithTextBox, продемонстрированный в следующем листинге.

Листинг 20-12: Метод WithTextBox, базирующийся на выражениях
public FluentForm<TForm> WithTextBox<TField>(
		Expression<Func<TForm, TField>> field,
		TField value)
{
	var name = ExpressionHelper.GetExpressionText(field);
	_browser.TextField(Find.ByName(name))
			.TypeText(value.ToString());
	return this;
}

Наш метод FluentForm (строки 1-3) содержит другой родовой параметр, TField, который помогает выполнять проверку значений формы во время компиляции. Первый параметр – это выражение, которое принимает объект типа TForm и возвращает экземпляр типа TField. Использование выражения для перехода к элементам типа – это общая практика для осуществления строго типизированной рефлексии. Второй параметр, типа TField, будет значением, устанавливаемым в элемент ввода данных.

Для корректного размещения элемента ввода данных, базирующегося на данном выражении, мы используем встроенный в ASP.NET MVC класс ExpressionHelper (строка 5) для того, чтобы сконструировать название элемента пользовательского интерфейса на основании выражения. Что касается нашего первоначального примера, то фрагмент кода form => form.Price приведет к созданию элемента ввода данных с названием "Price".

Наряду с корректным, сохраняемым при компиляции названием элемента ввода данных, мы используем объект IE для того, чтобы определить местоположение этого элемента по имени и ввести предоставляемое значение (строки 6-7). Наконец, для обеспечения связывания множественных полей ввода данных, мы возвращаем сам объект FluentForm.

Преимущества данного подхода – те же самые, что и при использовании строго типизированных представлений и генераторов HTML, базирующихся на выражениях. Мы можем выполнять рефакторинг объектов нашей модели с уверенностью в том, что, несмотря на любые изменения, наши представления будут соответствовать действительности. Благодаря совместному использованию данной методики в наших тестах пользовательского интерфейса, наши тесты больше не будут разрушаться при изменении нашей модели. Если мы удалим элемент из нашей модели представления – например, если он больше не отображается, – наш тест пользовательского интерфейса больше не будет компилироваться. Эту преждевременную реакцию на то, что что-то может измениться, легче обнаружить и урегулировать, нежели ждать провала теста.

После заполнения элемента ввода данных нам необходимо нажать на кнопку Save при помощи нашего метода Save, как это продемонстрировано ниже:

Листинг 20-13: Метод FluentForm Save
public void Save()
{
	_browser.Forms[0].Submit();
}

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

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