Поделиться через


Модульное тестирование

Совет

Это содержимое является фрагментом из электронной книги, шаблонов корпоративных приложений с помощью .NET MAUI, доступных в .NET Docs или в виде бесплатного скачиваемого PDF-файла, который можно прочитать в автономном режиме.

Корпоративные шаблоны приложений с помощью эскиза обложки электронной книги .NET MAUI .

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

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

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

Примечание.

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

Модульные тесты обычно используют шаблон упорядочения-act-assert:

Этап Description
Упорядочить Инициализирует объекты и задает значение данных, передаваемых в тестируемый метод.
Действие Вызывает метод, тестируемый с необходимыми аргументами.
Assert Проверяет, работает ли действие метода, выполняемого в тесте, как ожидалось.

Этот шаблон гарантирует, что модульные тесты доступны для чтения, самоописания и согласованности.

Внедрение зависимостей и модульное тестирование

Одна из мотивов внедрения слабо связанной архитектуры заключается в том, что она упрощает модульное тестирование. Одним из типов, зарегистрированных в службе внедрения зависимостей, является 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 для выполнения модульного тестирования, которое поддерживает два различных типа модульных тестов:

Тип тестирования Атрибут Description
TestMethod TestMethod Определяет фактический метод тестирования для запуска.
DataSource 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> свойства в экземпляре MockViewModel имеют данные.

А также проверка успешности проверки, модульные тесты проверки также должны проверять значения ValueIsValidErrors и свойства каждого 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 не имеет данных, а ValueIsValidErrors свойство и свойство каждого ValidatableObject<T> экземпляра задано правильно.

Итоги

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

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

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