Модульного тестирования корпоративных приложенийUnit Testing Enterprise Apps

Мобильные приложения имеют уникальные проблемы, настольных и веб-приложений не нужно беспокоиться о.Mobile apps have unique problems that desktop and web-based applications don't have to worry about. Мобильные пользователи будут отличаться по устройства, которые они используют, в сетевом подключении, доступность служб, а также ряд других факторов.Mobile users will differ by the devices that they use, by network connectivity, by the availability of services, and a range of other factors. Таким образом мобильные приложения должны тестироваться, так как они понадобятся в реальном мире для улучшения их качества, надежности и производительности.Therefore, mobile apps should be tested as they will be used in the real world to improve their quality, reliability, and performance. Существует много типов тестирования, которые должны выполняться для приложения, включая модульное тестирование, тестирование интеграции и тестирования с модульным тестированием, что наиболее распространенная форма тестирования пользовательского интерфейса.There are many types of testing that should be performed on an app, including unit testing, integration testing, and user interface testing, with unit testing being the most common form of testing.

Модульный тест занимает небольшое устройство приложения, обычно метод и изолировать ее оставшаяся часть кода проверяет, что она правильно работает.A unit test takes a small unit of the app, typically a method, isolates it from the remainder of the code, and verifies that it behaves as expected. Наша цель — проверить, что каждая единица функции выполняет надлежащим образом, позволяющий ошибки не распространились в приложение.Its goal is to check that each unit of functionality performs as expected, so that errors don't propagate throughout the app. Обнаружение ошибок, где они происходят более эффективен, наблюдая за последствия ошибку косвенно на вторичный точки сбоя.Detecting a bug where it occurs is more efficient that observing the effect of a bug indirectly at a secondary point of failure.

Модульное тестирование максимально влияет на качество кода при является неотъемлемой частью рабочего процесса разработки программного обеспечения.Unit testing has the greatest effect on code quality when it's an integral part of the software development workflow. Как только метод записана, модульные тесты должны быть написаны для проверки поведения метода в ответ на стандартные, граничные и некорректные случаи ввода данных, и что эта проверка явные или предполагаемые допущения, сделанные кодом.As soon as a method has been written, unit tests should be written that verify the behavior of the method in response to standard, boundary, and incorrect cases of input data, and that check any explicit or implicit assumptions made by the code. Кроме того с Разработка на основе тестирования, модульные тесты создаются прежде чем код.Alternatively, with test driven development, unit tests are written before the code. В этом случае модульные тесты в качестве технической документации и спецификации функциональности.In this scenario, unit tests act as both design documentation and functional specifications.

Примечание

Модульные тесты — очень эффективна против регрессии — то есть функциональные возможности, предназначенных для работы, но были задействуются по сбоя во время обновления.Unit tests are very effective against regression – that is, functionality that used to work but has been disturbed by a faulty update.

Модульные тесты обычно используется шаблон расположение act утверждение:Unit tests typically use the arrange-act-assert pattern:

  • Упорядочить разделе метода модульного тестирования инициализирует объекты и устанавливает значение данных, который передается методу для теста.The arrange section of the unit test method initializes objects and sets the value of the data that is passed to the method under test.
  • Действовать разделе вызывает метод для теста с необходимым набором аргументов.The act section invokes the method under test with the required arguments.
  • Assert раздел проверяет, что метод для теста действовал, должным образом.The assert section verifies that the action of the method under test behaves as expected.

Следовать этому шаблону гарантирует, что модульные тесты доступны для чтения и согласованность.Following this pattern ensures that unit tests are readable and consistent.

Внедрение зависимостей и модульное тестированиеDependency Injection and Unit Testing

Одним из этих причин заимствования используется слабо связанная архитектура является, что она упрощает модульное тестирование.One of the motivations for adopting a loosely-coupled architecture is that it facilitates unit testing. Одним из типов, зарегистрированных с помощью Autofac является OrderService класса.One of the types registered with Autofac is the OrderService class. В следующем примере кода показано структуры этого класса:The following code example shows an outline of this class:

public class OrderDetailViewModel : ViewModelBase  
{  
    private IOrderService _ordersService;  

    public OrderDetailViewModel(IOrderService ordersService)  
    {  
        _ordersService = ordersService;  
    }  
    ...  
}

OrderDetailViewModel Класс имеет зависимость от IOrderService типа контейнера разрешается, когда он создает экземпляр OrderDetailViewModel объекта.The OrderDetailViewModel class has a dependency on the IOrderService type which the container resolves when it instantiates a OrderDetailViewModel object. Тем не менее не нужно создавать OrderService модульный тест, который OrderDetailViewModel класса, замените, OrderService объект с макет для тестов.However, rather than create an OrderService object to unit test the OrderDetailViewModel class, instead, replace the OrderService object with a mock for the purpose of the tests. Данная связь показана на рис. 10-1.Figure 10-1 illustrates this relationship.

Рис. 10-1. Классы, реализующие интерфейс IOrderServiceFigure 10-1: Classes that implement the IOrderService interface

Такой подход позволяет OrderService объекту быть переданным в OrderDetailViewModel классов во время выполнения и в целях тестирования, она позволяет OrderMockService класс, передаваемый в OrderDetailViewModel класса во время тестирования.This approach allows the OrderService object to be passed into the OrderDetailViewModel class at runtime, and in the interests of testability, it allows the OrderMockService class to be passed into the OrderDetailViewModel class at test time. Основное преимущество этого подхода заключается том, что он позволяет модульных тестов для выполнения без необходимости громоздким ресурсы, такие как веб-службы или базы данных.The main advantage of this approach is that it enables unit tests to be executed without requiring unwieldy resources such as web services, or databases.

Тестирование приложений MVVMTesting MVVM Applications

Тестирование модели и модели представлений из приложений MVVM идентична тестирование любые другие классы, и те же средства и методы — например модульного тестирования и макетирования, может использоваться.Testing models and view models from MVVM applications is identical to testing any other classes, and the same tools and techniques – such as unit testing and mocking, can be used. Тем не менее существуют некоторые шаблоны, что характерно для модели и классов модели представления, которые могут использовать преимущества методики тестирования для конкретного устройства.However, there are some patterns that are typical to model and view model classes, that can benefit from specific unit testing techniques.

Совет

Протестируйте одну вещь, с каждым модульным тестом.Test one thing with each unit test. Не следует соблазняться позволит провести модульное тестирование упражнении более чем одним из аспектов поведения устройства.Don't be tempted to make a unit test exercise more than one aspect of the unit's behavior. Это ведет к тесты, которые сложно читать и обновлять.Doing so leads to tests that are difficult to read and update. Он может также привести к путанице при интерпретации сбоя.It can also lead to confusion when interpreting a failure.

Использование мобильного приложения eShopOnContainers xUnit для выполнения модульного тестирования, которое поддерживает два различных типа модульных тестов:The eShopOnContainers mobile app uses xUnit to perform unit testing, which supports two different types of unit tests:

  • Факты, тесты, которые всегда являются значение равно true, какой тест неизменяемых условий.Facts are tests that are always true, which test invariant conditions.
  • Теорий, тесты, которые выполняются только для определенного набора данных.Theories are tests that are only true for a particular set of data.

Модульные тесты, включенные в мобильном приложении eShopOnContainers, тесты фактов, и поэтому каждый метод модульного теста снабжен [Fact] атрибута.The unit tests included with the eShopOnContainers mobile app are fact tests, and so each unit test method is decorated with the [Fact] attribute.

Примечание

Средство выполнения тестов, выполняемых тестов xUnit.xUnit tests are executed by a test runner. Для выполнения тестов, запустите проект eShopOnContainers.TestRunner для платформы, необходимые.To execute the test runner, run the eShopOnContainers.TestRunner project for the required platform.

Тестирование асинхронные функцииTesting Asynchronous Functionality

При реализации шаблона MVVM, Просмотр моделей обычно операции служб, часто асинхронного вызова.When implementing the MVVM pattern, view models usually invoke operations on services, often asynchronously. Тесты для кода, вызывающего эти операции обычно служит замены макеты для служб.Tests for code that invokes these operations typically use mocks as replacements for the actual services. В следующем примере кода показано тестирование асинхронных функциональных возможностей, передав услуга макетов в модель представления:The following code example demonstrates testing asynchronous functionality by passing a mock service into a view model:

[Fact]  
public async Task OrderPropertyIsNotNullAfterViewModelInitializationTest()  
{  
    var orderService = new OrderMockService();  
    var orderViewModel = new OrderDetailViewModel(orderService);  

    var order = await orderService.GetOrderAsync(1, GlobalSetting.Instance.AuthToken);  
    await orderViewModel.InitializeAsync(order);  

    Assert.NotNull(orderViewModel.Order);  
}

Этот модульный тест проверяет, что Order свойство OrderDetailViewModel экземпляр будет иметь значение после InitializeAsync метод был вызван.This unit test checks that the Order property of the OrderDetailViewModel instance will have a value after the InitializeAsync method has been invoked. InitializeAsync Метод вызывается при переходе в соответствующее представление модели представления.The InitializeAsync method is invoked when the view model's corresponding view is navigated to. Дополнительные сведения о навигации, см. в разделе навигации.For more information about navigation, see Navigation.

Когда OrderDetailViewModel создается экземпляр, ожидается, что OrderService можно определить в качестве аргумента экземпляр.When the OrderDetailViewModel instance is created, it expects an OrderService instance to be specified as an argument. Тем не менее OrderService извлекает данные из веб-службы.However, the OrderService retrieves data from a web service. Таким образом OrderMockService экземпляра, то есть имитационную версию объекта OrderService класса, указанное в качестве аргумента для OrderDetailViewModel конструктор.Therefore, an OrderMockService instance, which is a mock version of the OrderService class, is specified as the argument to the OrderDetailViewModel constructor. Затем, когда модель представления InitializeAsync вызывается метод, который вызывает IOrderService фиктивных данных операций, полученных, а не взаимодействует с веб-службы.Then, when the view model's InitializeAsync method is invoked, which invokes IOrderService operations, mock data is retrieved rather than communicating with a web service.

Тестирование реализации INotifyPropertyChangedTesting INotifyPropertyChanged Implementations

Реализация INotifyPropertyChanged интерфейс позволяет представления, чтобы реагировать на изменения, которые получены из представления модели и модели.Implementing the INotifyPropertyChanged interface allows views to react to changes that originate from view models and models. Эти изменения не только данные, отображаемые в элементах управления – они также используются для управления представления, например состояний модели представления, вызывающие анимации для запуска или элементы управления, должно быть отключено.These changes are not limited to data shown in controls – they are also used to control the view, such as view model states that cause animations to be started or controls to be disabled.

Свойства, которые могут обновляться непосредственно через модульного теста можно протестировать путем присоединения обработчика событий к PropertyChanged событий и проверки, нужно ли создавать события после установки нового значения для свойства.Properties that can be updated directly by the unit test can be tested by attaching an event handler to the PropertyChanged event and checking whether the event is raised after setting a new value for the property. В следующем примере кода показан такой тест:The following code example shows such a test:

[Fact]  
public async Task SettingOrderPropertyShouldRaisePropertyChanged()  
{  
    bool 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.True(invoked);  
}

Этот модульный тест вызывает InitializeAsync метод OrderViewModel класса, что приводит его Order обновляемое свойство.This unit test invokes the InitializeAsync method of the OrderViewModel class, which causes its Order property to be updated. Модульный тест выполняется успешно, при условии, что PropertyChanged события для Order свойство.The unit test will pass, provided that the PropertyChanged event is raised for the Order property.

Тестирование обмена сообщениямиTesting Message-based Communication

Представление модели, использующие MessagingCenter класс для взаимодействия между классами слабосвязанных единицы тестируются путем подписки на сообщения, отправляемого с кодом, как показано в следующем примере кода:View models that use the MessagingCenter class to communicate between loosely-coupled classes can be unit tested by subscribing to the message being sent by the code under test, as demonstrated in the following code example:

[Fact]  
public void AddCatalogItemCommandSendsAddProductMessageTest()  
{  
    bool messageReceived = false;  
    var catalogService = new CatalogMockService();  
    var catalogViewModel = new CatalogViewModel(catalogService);  

    Xamarin.Forms.MessagingCenter.Subscribe<CatalogViewModel, CatalogItem>(  
        this, MessageKeys.AddProduct, (sender, arg) =>  
    {  
        messageReceived = true;  
    });  
    catalogViewModel.AddCatalogItemCommand.Execute(null);  

    Assert.True(messageReceived);  
}

Этот модульный тест проверяет, что CatalogViewModel публикует AddProduct сообщение в ответ на его AddCatalogItemCommand выполняемой.This unit test checks that the CatalogViewModel publishes the AddProduct message in response to its AddCatalogItemCommand being executed. Так как MessagingCenter класс поддерживает сообщения многоадресной передачи подписок, модульного теста можно подписаться на AddProduct сообщений и выполнения делегат обратного вызова в ответ на его получение.Because the MessagingCenter class supports multicast message subscriptions, the unit test can subscribe to the AddProduct message and execute a callback delegate in response to receiving it. Этот делегат обратного вызова, заданный как лямбда-выражение, задает boolean поле, которое используется Assert инструкцию, чтобы проверить поведение теста.This callback delegate, specified as a lambda expression, sets a boolean field that's used by the Assert statement to verify the behavior of the test.

Тестирование обработки исключенийTesting Exception Handling

Модульные тесты можно также записать, убедитесь, что определенные исключения для недопустимые действия или входные данные, как показано в следующем примере кода:Unit tests can also be written that check that specific exceptions are thrown for invalid actions or inputs, as demonstrated in the following code example:

[Fact]  
public void InvalidEventNameShouldThrowArgumentExceptionText()  
{  
    var behavior = new MockEventToCommandBehavior  
    {  
        EventName = "OnItemTapped"  
    };  
    var listView = new ListView();  

    Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));  
}

Этот модульный тест приведет к возникновению исключения, так как ListView управления не имеет событие с именем OnItemTapped.This unit test will throw an exception, because the ListView control does not have an event named OnItemTapped. Assert.Throws<T> Метод является универсальным методом где T — это тип ожидаемого исключения.The Assert.Throws<T> method is a generic method where T is the type of the expected exception. Аргумент, переданный в Assert.Throws<T> метод является лямбда-выражение, которое вызовет исключение.The argument passed to the Assert.Throws<T> method is a lambda expression that will throw the exception. Таким образом, пройдет модульного теста, при условии, что лямбда-выражение вызывает ArgumentException.Therefore, the unit test will pass provided that the lambda expression throws an ArgumentException.

💡 Совет: Избегайте написания модульных тестов, которые анализируют строки сообщения исключений.💡 Tip: Avoid writing unit tests that examine exception message strings. Строки сообщения исключений могут изменяться с течением времени, и поэтому модульные тесты, которые полагаются на их наличие считаются значительно усложняло работу.Exception message strings might change over time, and so unit tests that rely on their presence are regarded as brittle.

Тестирование проверкиTesting Validation

Существует два аспекта тестирование реализации проверки: тестирование, что правильно реализованы в все правила проверки и тестирования, ValidatableObject<T> класс выполняет должным образом.There are two aspects to testing the validation implementation: testing that any validation rules are correctly implemented, and testing that the ValidatableObject<T> class performs as expected.

Логика проверки обычно прост для тестирования, так как это обычно автономные процесса, где выходных данных зависит от входных данных.Validation logic is usually simple to test, because it is typically a self-contained process where the output depends on the input. В результаты вызова должен быть тесты Validate метод в каждом свойстве, имеет по крайней мере один связанное правило проверки, как показано в следующем примере кода:There should be tests on the results of invoking the Validate method on each property that has at least one associated validation rule, as demonstrated in the following code example:

[Fact]  
public void CheckValidationPassesWhenBothPropertiesHaveDataTest()  
{  
    var mockViewModel = new MockViewModel();  
    mockViewModel.Forename.Value = "John";  
    mockViewModel.Surname.Value = "Smith";  

    bool isValid = mockViewModel.Validate();  

    Assert.True(isValid);  
}

Этот модульный тест проверяет, что проверка проходит успешно, когда два ValidatableObject<T> свойств в MockViewModel оба экземпляра имеют данных.This unit test checks that validation succeeds when the two ValidatableObject<T> properties in the MockViewModel instance both have data.

А также проверки, что проверка прошла успешно, модульные тесты для проверки следует также проверить значения Value, IsValid, и Errors свойства каждого ValidatableObject<T> экземпляр, чтобы убедиться, что класс выполняет должным образом.As well as checking that validation succeeds, validation unit tests should also check the values of the Value, IsValid, and Errors property of each ValidatableObject<T> instance, to verify that the class performs as expected. В следующем примере кода демонстрируется модульный тест, который делает это:The following code example demonstrates a unit test that does this:

[Fact]  
public void CheckValidationFailsWhenOnlyForenameHasDataTest()  
{  
    var mockViewModel = new MockViewModel();  
    mockViewModel.Forename.Value = "John";  

    bool isValid = mockViewModel.Validate();  

    Assert.False(isValid);  
    Assert.NotNull(mockViewModel.Forename.Value);  
    Assert.Null(mockViewModel.Surname.Value);  
    Assert.True(mockViewModel.Forename.IsValid);  
    Assert.False(mockViewModel.Surname.IsValid);  
    Assert.Empty(mockViewModel.Forename.Errors);  
    Assert.NotEmpty(mockViewModel.Surname.Errors);  
}

Этот модульный тест проверяет, что при сбое проверки Surname свойство MockViewModel не содержит данных и Value, IsValid, и Errors свойства каждого ValidatableObject<T> экземпляр заданы правильно.This unit test checks that validation fails when the Surname property of the MockViewModel doesn't have any data, and the Value, IsValid, and Errors property of each ValidatableObject<T> instance are correctly set.

СводкаSummary

Модульный тест занимает небольшое устройство приложения, обычно метод и изолировать ее оставшаяся часть кода проверяет, что она правильно работает.A unit test takes a small unit of the app, typically a method, isolates it from the remainder of the code, and verifies that it behaves as expected. Наша цель — проверить, что каждая единица функции выполняет надлежащим образом, позволяющий ошибки не распространились в приложение.Its goal is to check that each unit of functionality performs as expected, so that errors don't propagate throughout the app.

Поведение объекта тестируемого могут быть изолированы, заменив зависимых объектов в макеты объектов, которые имитируют поведение зависимые объекты.The behavior of an object under test can be isolated by replacing dependent objects with mock objects that simulate the behavior of the dependent objects. Это позволяет модульных тестов для выполнения без необходимости громоздким ресурсы, такие как веб-службы или базы данных.This enables unit tests to be executed without requiring unwieldy resources such as web services, or databases.

Тестирование модели и модели представлений из приложений MVVM идентична тестирование любые другие классы, и можно использовать те же средства и методы.Testing models and view models from MVVM applications is identical to testing any other classes, and the same tools and techniques can be used.