Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Возьмем тот же проект из прошлый темы (либо создадим новый) и изменим в главном проекте код контроллера HomeController:
using Microsoft.AspNetCore.Mvc; namespace UnitTestApp.Controllers { public class HomeController : Controller { public IActionResult Index() { ViewData["Message"] = "Hello!"; return View("Index"); } } }
Контроллер имеет только один метод, который устанавливает значение ViewData["Message"]
и генерирует объект ViewResult, который использует представление Index. Теперь протестируем этот метод.
Для этого в проект тестов добавим новый класс, который назовем HomeControllerTests:
using Microsoft.AspNetCore.Mvc; using UnitTestApp.Controllers; using Xunit; namespace UnitTestApp.Tests { public class HomeControllerTests { [Fact] public void IndexViewDataMessage() { // Arrange HomeController controller = new HomeController(); // Act ViewResult result = controller.Index() as ViewResult; // Assert Assert.Equal("Hello world!", result?.ViewData["Message"]); } [Fact] public void IndexViewResultNotNull() { // Arrange HomeController controller = new HomeController(); // Act ViewResult result = controller.Index() as ViewResult; // Assert Assert.NotNull(result); } [Fact] public void IndexViewNameEqualIndex() { // Arrange HomeController controller = new HomeController(); // Act ViewResult result = controller.Index() as ViewResult; // Assert Assert.Equal("Index", result?.ViewName); } } }
Этот простейший стандартный тест, который призван дать начальное понимание работы тестов.
Метод IndexViewResultNotNull
тестирует результат метода - возвращаемый объект ViewResult не должен иметь значение null.
Метод IndexViewNameEqualIndex
проверяет название вызываемого представления с помощью вызова Assert.Equal.
А метод IndexViewDataMessage
проверяет значение по ключу "Message" в словаре ViewData.
Тесты в xUnit определяются в виде методов, к которым применяются атрибуты Fact и Theory. Атрибут Fact указывает, что методы представляет тест.
Каждый метод содержит три логических части - Arrange, Act и Assert. Они помечены соответствующими комментариями. Вкратце рассмотрим, что они делают.
Секция Arrange выполняет начальную инициализацию контекста тестов, а именно создает объект контроллера: HomeController controller = new HomeController()
.
Далее в секции Act выполняет само действие, которое надо протестировать, а именно генерация представления: ViewResult result = controller.Index() as ViewResult
.
Так как метод Index контроллера возвращает объект ActionResult, то его еще надо привести к объекту ViewResult.
Чтобы проверить результат текста в секции Assert вызывается один из методов класса Assert
.
Модель тестов Arrange-Act-Assert представляет целую парадигму тестирования, которая используется многими фреймворками юнит-тестов:
Arrange: устанавливает начальные условия для выполнения теста
Act: выполняет тест (обычно представляет одну строку кода)
Assert: верифицирует результат теста
Секции Arrange и Act представляют обычный код на языке C#. А секция Assert использует одноименный класс Assert, который определен в библиотеке xUnit.net.
Для проверки результата в классе Assert определено ряд методов, среди которых можно выделить следующие:
All(collection, action): метод подтверждает, что все элементы коллекции collection соответствуют действию action
Contains(expectedSubString, actualString): метод подтверждает, что строка actualString содержит expectedSubString
DoesNotContain(expectedSubString, actualString): метод подтверждает, что строка actualString не содержит строку expectedSubString
DoesNotMatch(expectedRegexPattern, actualString): метод подтверждает, что строка actualString не соответствует регулярному выражению expectedRegexPattern
Matches(expectedRegexPattern, actualString): метод подтверждает, что строка actualString соответствует регулярному выражению expectedRegexPattern
Equal(expected, result): метод сравнивает результат теста в виде значения result
и ожидаемое значение
expected
и подтверждает их равенство
NotEqual(expected, result): метод сравнивает результат теста в виде значения result
и ожидаемое значение
expected
и подтверждает их неравенство
Empty(collection): метод подтверждает, что коллекция collection пустая
NotEmpty(collection): метод подтверждает, что коллекция collection не пустая
True(result): метод подтверждает, что результат теста равен true
False(result): метод подтверждает, что результат теста равен false
IsType(expected, result): метод подтверждает, что результат теста имеет тип expected
IsNotType(expected, result): метод подтверждает, что результат теста не представляет тип expected
IsNull(result): метод подтверждает, что результат теста имеет значение null
IsNotNull(result): метод подтверждает, что результат теста не равен null
InRange(result, low, high): метод подтверждает, что результат теста находится в диапазоне между low и high
NotInRange(result, low, high): метод подтверждает, что результат теста не принадлежит диапазону от low до high
Same(expected, actual): метод подтверждает, что ссылки expected
и actual
указывают на один и тот же объект в памяти
NotSame(expected, actual): метод подтверждает, что ссылки expected
и actual
указывают на разные объекты в памяти
Throws(exception, expression): метод подтверждает, что выражение expression
генерирует исключение exception
Теперь запустим тест на выполнение через меню Test->Run All Tests:
После этого откроется окно Test Explorer или обозреватель тестов. Если все нормально, то обозреватель тестов сигнализирует нам зеленым цветом, что все тесты успешно пройдены. Если же нет, то не пройденные тесты будут отмечены красным цветом.
В данном случае после запуска тестов мы увидим, что один тест - IndexViewDataMessage не пройден. Потому что мы ожидаем, что ViewData["Message"]
будет иметь значение
"Hello world!". Однако в методе Index контроллера HomeController передается совсем другое значение: ViewData["Message"] = "Hello!"
. То есть
имеющаяся логика не соответствует нашим ожиданиям, и в нем имеется ошибка. Теперь изменим код метода Index:
public class HomeController : Controller { public IActionResult Index() { ViewData["Message"] = "Hello world!"; return View("Index"); } }
И заново запустим тесты:
Теперь все тесты пройдены.
В принципе необязательно было бы разделять все ключевые действия по разным методам. Можно было бы объединить все тесты в один:
[Fact] public void IndexTest() { // Arrange HomeController controller = new HomeController(); // Act ViewResult result = controller.Index() as ViewResult; // Assert Assert.Equal("Hello world!", result?.ViewData["Message"]); Assert.NotNull(result); Assert.Equal("Index", result?.ViewName); }
В этом случае тест был пройден, если подтверждены все используемые выражения Assert.