Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Совет
Это содержимое является фрагментом из электронной книги, шаблонов корпоративных приложений с помощью .NET MAUI, доступных в .NET Docs или в виде бесплатного скачиваемого PDF-файла, который можно прочитать в автономном режиме.
MAUI eBook cover thumbnail.
Проблемы с мультиплатформенными приложениями похожи на классические и веб-приложения. Мобильные пользователи будут отличаться по устройствам, сетевому подключению, доступности служб и различным другим факторам. Таким образом, многоплатформенные приложения должны тестироваться так, как они будут использоваться в реальном мире для улучшения качества, надежности и производительности. Многие типы тестирования должны выполняться в приложении, включая модульное тестирование, тестирование интеграции и тестирование пользовательского интерфейса. Модульное тестирование является наиболее распространенной формой и важной для создания высококачественных приложений.
Модульный тест принимает небольшую единицу приложения, как правило, метод, изолирует его от остальной части кода и проверяет, работает ли оно должным образом. Его целью является проверка того, что каждая единица функциональных возможностей выполняется должным образом, поэтому ошибки не распространяются по всему приложению. Обнаружение ошибки там, где она возникает, более эффективно, чем косвенное наблюдение за её эффектом в во вторичной точке сбоя.
Модульное тестирование оказывает наибольшее влияние на качество кода, если это неотъемлемая часть рабочего процесса разработки программного обеспечения. Модульные тесты могут выступать в качестве документации по проектированию и функциональным спецификациям для приложения. Как только метод написан, необходимо написать модульные тесты, которые проверяют поведение метода в ответ на стандартные, граничные и неправильные входные данные, а также проверяют любые явные и неявные допущения, сделанные кодом. Кроме того, при разработке на основе тестов модульные тесты записываются перед кодом. Дополнительные сведения о разработке на основе тестов и его реализации см. в статье Пошаговое руководство. Разработка на основе тестов с помощью обозревателя тестов..
Примечание.
Модульные тесты очень эффективны против регрессии. То есть функциональность, которая раньше работала, но была нарушена некорректным обновлением.
Модульные тесты обычно используют шаблон инициализация-действие-проверка:
| Этап | Описание |
|---|---|
| Упорядочить | Инициализирует объекты и задает значение данных, передаваемых в тестируемый метод. |
| Действие | Вызывает метод, тестируемый с необходимыми аргументами. |
| утверждать | Проверяет, работает ли действие метода, выполняемого в тесте, как ожидалось. |
Этот шаблон гарантирует, что модульные тесты читаемы, самодокументируемы и согласованы.
Внедрение зависимостей и модульное тестирование
Одна из мотивов внедрения слабо связанной архитектуры заключается в том, что она упрощает модульное тестирование. Одним из типов, зарегистрированных в службе внедрения зависимостей, является интерфейс IAppEnvironmentService. В следующем примере кода показана структура этого класса:
public class OrderDetailViewModel : ViewModelBase
{
private IAppEnvironmentService _appEnvironmentService;
public OrderDetailViewModel(
IAppEnvironmentService appEnvironmentService,
IDialogService dialogService, INavigationService navigationService, ISettingsService settingsService)
: base(dialogService, navigationService, settingsService)
{
_appEnvironmentService = appEnvironmentService;
}
}
Класс OrderDetailViewModel имеет зависимость от типа IAppEnvironmentService, которую контейнер внедрения зависимостей разрешает при создании экземпляра объекта OrderDetailViewModel. Однако вместо создания IAppEnvironmentService объекта, который использует реальные серверы, устройства и конфигурации для модульного тестирования OrderDetailViewModel класса, вместо этого замените IAppEnvironmentService объект макетом для целей тестов. Объект макета — это объект, имеющий ту же сигнатуру объекта или интерфейса, но создается определенным образом, чтобы помочь в модульном тестировании. Он часто используется с внедрением зависимостей для предоставления конкретных реализаций интерфейсов для тестирования различных сценариев данных и рабочих процессов.
Этот подход позволяет передавать объект IAppEnvironmentService в класс OrderDetailViewModel во время выполнения, а для удобства тестирования позволяет передавать макетный класс в класс OrderDetailViewModel во время тестирования. Основное преимущество этого подхода заключается в том, что он позволяет выполнять модульные тесты без необходимости использовать неуправляемые ресурсы, такие как функции платформы среды выполнения, веб-службы или базы данных.
Тестирование приложений MVVM
Тестирование моделей и моделей просмотра из приложений MVVM идентично тестированию любого другого класса и использует те же средства и методы; сюда входят такие функции, как модульное тестирование и макетирование. Однако некоторые шаблоны, типичные для классов моделей и моделей представления, могут воспользоваться определенными методами модульного тестирования.
Совет
Протестируйте одну вещь с каждым модульным тестом. По мере расширения сложности теста он затрудняет проверку этого теста. Ограничив модульный тест одной проблемой, мы можем убедиться, что наши тесты являются более повторяемыми, изолированными и имеют меньшее время выполнения. Рекомендации по модульному тестированию с .NET см. в лучших практиках по модульному тестированию для больше лучших практик.
Не поддавайтесь искушению делать модульный тест проверкой более чем одного аспекта поведения модуля. Это приводит к тестам, которые трудно читать и обновлять. Это также может привести к путанице при интерпретации сбоя.
Приложение eShop с несколькими платформами использует MSTest для выполнения модульного тестирования, которое поддерживает два различных типа модульных тестов:
| Тип тестирования | Атрибут | Описание |
|---|---|---|
| ТестовыйМетод | TestMethod |
Определяет конкретный метод тестирования, который будет выполняться. |
| Источник данных | DataSource |
Тесты, которые соответствуют только определенному набору данных. |
Модульные тесты, включенные в мультиплатформенное приложение eShop, соответствуют TestMethod, поэтому каждый метод модульного теста аннотирован атрибутом TestMethod. Помимо MSTest есть несколько других платформ тестирования, включая NUnit и xUnit.
Тестирование асинхронных функций
При реализации шаблона MVVM модели представления обычно вызывают операции со службами, часто асинхронно. Тесты кода, вызывающего эти операции, обычно используют макеты в качестве замены фактических служб. В следующем примере кода демонстрируется тестирование асинхронной функциональности путем передачи макетной службы в модель представления:
[TestMethod]
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest()
{
// Arrange
var orderService = new OrderMockService();
var orderViewModel = new OrderDetailViewModel(orderService);
// Act
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
await orderViewModel.InitializeAsync(order);
// Assert
Assert.IsNotNull(orderViewModel.Order);
}
Этот модульный тест проверяет, будет ли Order свойство OrderDetailViewModel экземпляра иметь значение после InitializeAsync вызова метода. Метод InitializeAsync вызывается при навигации к соответствующему представлению модели. Дополнительные сведения о навигации см. в этом разделе.
При создании экземпляра OrderDetailViewModel ожидается, что экземпляр IOrderService будет указан в качестве аргумента. Однако OrderService извлекает данные из веб-службы. Таким образом, экземпляр OrderMockService, являющийся макетом класса OrderService, указывается в качестве аргумента конструктора OrderDetailViewModel. Затем извлекаются тестовые данные, вместо обмена данными с веб-службой, при вызове метода модели представления InitializeAsync, который использует операции IOrderService.
Тестирование реализаций INotifyPropertyChanged
INotifyPropertyChanged Реализация интерфейса позволяет представлениям реагировать на изменения, поступающие из моделей и моделей представления. Эти изменения не ограничиваются данными, отображаемыми в элементах управления. Они также используются для управления представлением, например состояния модели представления, которые вызывают запуск анимации или отключение элементов управления.
Свойства, которые можно обновить непосредственно с помощью модульного теста, можно проверить, подключив обработчик событий к PropertyChanged событию и проверив, вызывается ли событие после задания нового значения для свойства. В следующем примере кода показан такой тест:
[TestMethod]
public async Task SettingOrderPropertyShouldRaisePropertyChanged()
{
var invoked = false;
var orderService = new OrderMockService();
var orderViewModel = new OrderDetailViewModel(orderService);
orderViewModel.PropertyChanged += (sender, e) =>
{
if (e.PropertyName.Equals("Order"))
invoked = true;
};
var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);
await orderViewModel.InitializeAsync(order);
Assert.IsTrue(invoked);
}
Этот юнит-тест вызывает метод InitializeAsync класса OrderViewModel, что приводит к обновлению свойства Order. Модульный тест пройдет, если событие PropertyChanged было вызвано для свойства Order.
Тестирование взаимодействия на основе сообщений
Модели представления, использующие класс MessagingCenter для обмена данными между слабо связанными классами, могут быть протестированы модульно путем подписки на сообщение, отправляемое тестируемым кодом, как показано в следующем примере кода.
[TestMethod]
public void AddCatalogItemCommandSendsAddProductMessageTest()
{
var messageReceived = false;
var catalogService = new CatalogMockService();
var catalogViewModel = new CatalogViewModel(catalogService);
MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(
this, MessageKeys.AddProduct, (sender, arg) =>
{
messageReceived = true;
});
catalogViewModel.AddCatalogItemCommand.Execute(null);
Assert.IsTrue(messageReceived);
}
Этот модульный тест проверяет, что CatalogViewModel опубликовывает сообщение AddProduct в ответ на выполнение AddCatalogItemCommand. Поскольку класс MessagingCenter поддерживает подписки на многоадресную рассылку сообщений, модульный тест может подписаться на сообщение и выполнить обратный вызов делегата в ответ на получение AddProduct. Этот делегат обратного вызова, указанный как лямбда-выражение, задает логическое поле, используемое Assert инструкцией для проверки поведения теста.
Тестирование обработки исключений
Модульные тесты также можно записать, чтобы убедиться, что определенные исключения создаются для недопустимых действий или входных данных, как показано в следующем примере кода:
[TestMethod]
public void InvalidEventNameShouldThrowArgumentExceptionText()
{
var behavior = new MockEventToCommandBehavior
{
EventName = "OnItemTapped"
};
var listView = new ListView();
Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));
}
Этот модульный тест вызовет исключение, так как элемент ListView управления не имеет события с именем OnItemTapped. Этот Assert.Throws<T> метод является универсальным методом, где T является тип ожидаемого исключения. Аргумент, переданный методу Assert.Throws<T> , является лямбда-выражением, которое вызовет исключение. Таким образом, модульный тест будет проходить, если лямбда-выражение выбрасывает исключение ArgumentException.
Совет
Избегайте написания модульных тестов, которые проверяют строки сообщения об исключении. Строки сообщений об исключении могут меняться со временем, и поэтому модульные тесты, которые полагаются на их присутствие, считаются хрупкими.
Валидация тестирования
Существует два аспекта для тестирования реализации проверки: проверка правильности реализации любых правил проверки и проверка того, что ValidatableObject<T> класс выполняется должным образом.
Логика проверки обычно проста для тестирования, так как обычно это автономный процесс, в котором выходные данные зависят от входных данных. Необходимо проверить результаты вызова Validate метода для каждого свойства, имеющего по крайней мере одно связанное правило проверки, как показано в следующем примере кода:
[TestMethod]
public void CheckValidationPassesWhenBothPropertiesHaveDataTest()
{
var mockViewModel = new MockViewModel();
mockViewModel.Forename.Value = "John";
mockViewModel.Surname.Value = "Smith";
var isValid = mockViewModel.Validate();
Assert.IsTrue(isValid);
}
Этот модульный тест проверяет, что проверка завершается успешно, когда оба свойства в экземпляре ValidatableObject<T> содержат данные.
Помимо проверки успешности валидации, модульные тесты также должны проверять значения свойств Value, IsValid, и Errors каждого экземпляра ValidatableObject<T>, чтобы убедиться в том, что класс работает как ожидалось. В следующем примере кода показан модульный тест, который делает это:
[TestMethod]
public void CheckValidationFailsWhenOnlyForenameHasDataTest()
{
var mockViewModel = new MockViewModel();
mockViewModel.Forename.Value = "John";
bool isValid = mockViewModel.Validate();
Assert.IsFalse(isValid);
Assert.IsNotNull(mockViewModel.Forename.Value);
Assert.IsNull(mockViewModel.Surname.Value);
Assert.IsTrue(mockViewModel.Forename.IsValid);
Assert.IsFalse(mockViewModel.Surname.IsValid);
Assert.AreEqual(mockViewModel.Forename.Errors.Count(), 0);
Assert.AreNotEqual(mockViewModel.Surname.Errors.Count(), 0);
}
Этот модульный тест проверяет, что проверка завершается ошибкой, если свойство Surname объекта MockViewModel не имеет данных, и свойства Value, IsValid, и Errors каждого экземпляра ValidatableObject<T> заданы правильно.
Итоги
Модульный тест принимает небольшую единицу приложения, как правило, метод, изолирует его от остальной части кода и проверяет, работает ли оно должным образом. Его целью является проверка того, что каждая единица функциональных возможностей выполняется должным образом, поэтому ошибки не распространяются по всему приложению.
Поведение объекта под тестом можно изолировать, заменив зависимые объекты макетами, которые имитируют поведение зависимых объектов. Это позволяет выполнять модульные тесты, не требуя неуправляемых ресурсов, таких как функции платформы среды выполнения, веб-службы или базы данных
Тестирование моделей и моделей просмотра из приложений MVVM идентично тестированию любых других классов, а также используются те же средства и методы.