엔터프라이즈 앱을 테스트 하는 단위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 assert 패턴을 사용합니다.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 테스트 목적으로 mock 사용 하 여 개체입니다.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: IOrderService 인터페이스를 구현 하는 클래스Figure 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.

MVVM 응용 프로그램 테스트Testing 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 테스트는 test runner에서 실행 됩니다.xUnit tests are executed by a test runner. 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.

INotifyPropertyChanged 구현 테스트Testing 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

단위 테스트도 쓸 수 잘못 된 동작 또는 입력에 대 한 특정 예외가 throw 되는 확인 다음 코드 예제에서 설명한 것 처럼: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));  
}

이 단위 테스트 하기 때문에 예외를 throw 합니다는 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> 메서드는 예외를 throw 하는 람다 식입니다.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.