Share via


엔터프라이즈 앱 단위 테스트

참고 항목

이 전자책은 2017년 봄에 게시되었으며 그 이후로 업데이트되지 않았습니다. 이 책에는 귀중한 기본 많이 있지만 일부 자료는 구식입니다.

모바일 앱에는 데스크톱 및 웹 기반 애플리케이션에서 걱정할 필요가 없는 고유한 문제가 있습니다. 모바일 사용자는 사용하는 디바이스, 네트워크 연결, 서비스 가용성 및 기타 다양한 요인에 따라 달라집니다. 따라서 모바일 앱은 품질, 안정성 및 성능을 개선하기 위해 실제 환경에서 사용되므로 테스트해야 합니다. 단위 테스트, 통합 테스트 및 사용자 인터페이스 테스트를 포함하여 앱에서 수행해야 하는 다양한 유형의 테스트가 있으며 단위 테스트가 가장 일반적인 테스트 형태입니다.

단위 테스트는 앱의 작은 단위(일반적으로 메서드)를 사용하고, 코드의 나머지 부분에서 격리하고, 예상대로 동작하는지 확인합니다. 목표는 앱 전체에서 오류가 전파되지 않도록 각 기능 단위가 예상대로 수행되도록 검사 것입니다. 버그가 발생하는 위치를 감지하는 것이 보조 실패 지점에서 버그의 영향을 간접적으로 관찰하는 것보다 더 효율적입니다.

단위 테스트는 소프트웨어 개발 워크플로의 필수적인 부분인 경우 코드 품질에 가장 큰 영향을 미칩니다. 메서드가 작성되는 즉시 입력 데이터의 표준, 경계 및 잘못된 사례에 대한 응답으로 메서드의 동작을 확인하고 코드에서 명시적 또는 암시적 가정을 검사 단위 테스트를 작성해야 합니다. 또는 테스트 기반 개발을 통해 단위 테스트가 코드 앞에 기록됩니다. 이 시나리오에서 단위 테스트는 디자인 설명서와 기능 사양 모두의 역할을 합니다.

참고 항목

단위 테스트는 회귀에 매우 효과적입니다. 즉, 작동했지만 잘못된 업데이트로 인해 방해를 받은 기능입니다.

단위 테스트는 일반적으로 arrange-act-assert 패턴을 사용합니다.

  • 단위 테스트 메서드의 정렬 섹션은 개체를 초기화하고 테스트 중인 메서드에 전달되는 데이터의 값을 설정합니다.
  • act 섹션은 필수 인수를 사용하여 테스트 중인 메서드를 호출합니다.
  • 어설션 섹션에서는 테스트 중인 메서드의 동작이 예상대로 작동하는지 확인합니다.

이 패턴을 따르면 단위 테스트를 읽을 수 있고 일관됩니다.

종속성 주입 및 단위 테스트

느슨하게 결합된 아키텍처를 채택하는 동기 중 하나는 단위 테스트를 용이하게 한다는 것입니다. Autofac에 등록된 형식 중 하나는 클래스입니다 OrderService . 다음 코드 예제에서는 이 클래스의 개요를 보여 줍니다.

public class OrderDetailViewModel : ViewModelBase  
{  
    private IOrderService _ordersService;  

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

클래스는 OrderDetailViewModel 개체를 인스턴스화할 때 컨테이너가 확인하는 형식에 IOrderService 종속됩니다 OrderDetailViewModel . 그러나 클래스를 OrderService 단위 테스트 OrderDetailViewModel 하는 개체를 만드는 대신 테스트 목적으로 개체를 모의 개체로 바꿉 OrderService 니다. 그림 10-1에서는 이 관계를 보여 줍니다.

Classes that implement the IOrderService interface

그림 10-1: IOrderService 인터페이스를 구현하는 클래스

이 방법을 사용하면 OrderService 런타임에 개체를 클래스에 OrderDetailViewModel 전달할 수 있으며 테스트 가능성을 위해 테스트 시 클래스를 클래스에 OrderDetailViewModel 전달할 수 있습니다OrderMockService. 이 방법의 기본 장점은 웹 서비스 또는 데이터베이스와 같은 다루기 힘든 리소스 없이 단위 테스트를 실행할 수 있다는 것입니다.

MVVM 애플리케이션 테스트

MVVM 애플리케이션에서 모델 테스트 및 보기 모델은 다른 클래스를 테스트하는 것과 동일하며 단위 테스트 및 모의와 같은 동일한 도구와 기술을 사용할 수 있습니다. 그러나 모델 클래스를 모델링하고 보는 데 일반적인 몇 가지 패턴이 있으며 특정 단위 테스트 기술을 활용할 수 있습니다.

각 단위 테스트로 한 가지 사항을 테스트합니다. 단위 테스트 연습을 둘 이상의 단위 동작으로 만드는 것은 바람직하지 않습니다. 이렇게 하면 테스트를 업데이트하기 어려울 수 있습니다. 또한 오류를 해석할 때 혼동을 초래할 수 있습니다.

eShopOnContainers 모바일 앱은 두 가지 유형의 단위 테스트를 지원하는 단위 테스트를 수행합니다.

  • 팩트는 항상 참인 테스트로, 고정 조건을 테스트합니다.
  • 이론은 특정 데이터 집합에 대해서만 참인 테스트입니다.

eShopOnContainers 모바일 앱에 포함된 단위 테스트는 팩트 테스트이므로 각 단위 테스트 메서드는 특성으로 [Fact] 데코레이팅됩니다.

참고 항목

xUnit 테스트는 테스트 실행기에서 실행됩니다. 테스트 실행기를 실행하려면 필요한 플랫폼에 대해 eShopOnContainers.TestRunner 프로젝트를 실행합니다.

비동기 기능 테스트

MVVM 패턴을 구현할 때 뷰 모델은 일반적으로 비동기적으로 서비스에 대한 작업을 호출합니다. 이러한 작업을 호출하는 코드에 대한 테스트는 일반적으로 모의 버전을 실제 서비스의 대체 항목으로 사용합니다. 다음 코드 예제에서는 모의 서비스를 뷰 모델에 전달하여 비동기 기능을 테스트하는 방법을 보여 줍니다.

[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);  
}

이 단위 테스트는 InitializeAsync 메서드가 호출된 후 OrderDetailViewModel 인스턴스의 Order 속성에 값이 있는지 확인합니다. 뷰 모델의 해당 뷰를 탐색할 때 InitializeAsync 메서드가 호출됩니다. 탐색에 대한 자세한 내용은 탐색을 참조하세요.

OrderDetailViewModel 인스턴스가 만들어지면 OrderService 인스턴스가 인수로 지정되어야 합니다. 그러나 OrderService는 웹 서비스에서 데이터를 검색합니다. 따라서 OrderMockService 클래스의 모의 OrderService 버전인 인스턴스는 생성자에 대한 인수 OrderDetailViewModel 로 지정됩니다. 그런 다음, 작업을 호출하는 뷰 모델의 메서드가 호출 IOrderService 되면 웹 서비스와 통신하는 대신 모의 InitializeAsync 데이터가 검색됩니다.

INotifyPropertyChanged 구현 테스트

INotifyPropertyChanged 인터페이스를 구현하면 뷰 모델 및 모델에서 시작된 변경에 뷰가 대응할 수 있습니다. 이러한 변경 내용은 컨트롤에 표시된 데이터로 제한되지 않습니다. 애니메이션을 시작하거나 컨트롤을 사용하지 않도록 설정할 수 있는 뷰 모델 상태와 같이 보기를 제어하는 데도 사용됩니다.

단위 테스트에서 직접 업데이트할 수 있는 속성은 이벤트 처리기를 PropertyChanged 이벤트에 연결하고 속성의 새 값을 설정한 후 이벤트가 발생하는지 여부를 확인하여 테스트할 수 있습니다. 다음 코드 예제에서는 이러한 테스트를 보여 줍니다.

[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);  
}

이 단위 테스트는 OrderViewModel 클래스의 InitializeAsync 메서드를 호출합니다. 그러면 해당 Order 속성이 업데이트됩니다. 단위 테스트는 PropertyChanged 이벤트가 Order 속성에 대해 발생하는 경우 통과입니다.

메시지 기반 통신 테스트

MessagingCenter 클래스를 사용하여 느슨하게 결합된 클래스 간을 통신하는 뷰 모델은 다음 코드 예제와 같이 테스트 중인 코드에서 보내는 메시지를 구독하여 단위 테스트를 수행할 수 있습니다.

[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이 실행 중인 해당 AddCatalogItemCommand에 대한 응답으로 AddProduct 메시지를 게시하는지 확인합니다. MessagingCenter 클래스는 멀티캐스트 메시지 구독을 지원하므로 단위 테스트는 AddProduct 메시지를 구독하고 수신에 대한 응답으로 콜백 대리자를 실행할 수 있습니다. 람다 식으로 지정된 이 콜백 대리자는 문에서 테스트 동작을 Assert 확인하는 데 사용되는 필드를 설정합니다boolean.

예외 처리 테스트

다음 코드 예제와 같이 잘못된 작업 또는 입력에 대해 특정 예외가 throw되는지 확인하는 단위 테스트를 작성할 수도 있습니다.

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

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

이 단위 테스트는 컨트롤에 ListView 이름이 지정된 OnItemTapped이벤트가 없으므로 예외를 throw합니다. Assert.Throws<T> 메서드는 T가 예상되는 예외의 형식인 제네릭 메서드입니다. Assert.Throws<T> 메서드에 전달된 인수는 예외를 throw하는 람다 식입니다. 따라서 단위 테스트는 람다 식이 ArgumentException을 throw하는 경우 통과입니다.

예외 메시지 문자열을 검사하는 단위 테스트를 작성하지 마세요. 예외 메시지 문자열은 시간이 지남에 따라 변경될 수 있으므로 현재 상태에 의존하는 단위 테스트는 불안정한 것으로 간주됩니다.

유효성 검사 테스트

유효성 검사 구현을 테스트하는 두 가지 측면은 유효성 검사 규칙이 올바르게 구현되었는지 테스트하고 클래스가 ValidatableObject<T> 예상대로 수행하는지 테스트하는 것입니다.

일반적으로 출력이 입력에 따라 달라지는 자체 포함 프로세스이므로 유효성 검사 논리는 일반적으로 간단히 테스트할 수 있습니다. 다음 코드 예제와 같이 하나 이상의 연결된 유효성 검사 규칙이 있는 각 속성에 대해 Validate 메서드를 호출한 결과를 테스트해야 합니다.

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

    bool isValid = mockViewModel.Validate();  

    Assert.True(isValid);  
}

이 단위 테스트는 MockViewModel 인스턴스의 두 ValidatableObject<T> 속성에 모두 데이터가 있는 경우 유효성 검사가 성공하는지 확인합니다.

유효성 검사에 성공하는지 확인할 뿐만 아니라 유효성 검사 단위 테스트는 각 ValidatableObject<T> 인스턴스의 Value, IsValidErrors 속성 값을 확인하여 클래스가 예상대로 작동하는지 알아봅니다. 다음 코드 예제에서는 이 작업을 수행하는 단위 테스트를 보여 줍니다.

[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);  
}

이 단위 테스트는 의 속성에 MockViewModelSurname 속성에 데이터가 없고 각 ValidatableObject<T>Value, IsValidErrors 속성이 올바르게 설정되면 유효성 검사가 실패했는지 확인합니다.

요약

단위 테스트는 앱의 작은 단위(일반적으로 메서드)를 사용하고, 코드의 나머지 부분에서 격리하고, 예상대로 동작하는지 확인합니다. 목표는 앱 전체에서 오류가 전파되지 않도록 각 기능 단위가 예상대로 수행되도록 검사 것입니다.

종속 개체를 종속 개체의 동작을 시뮬레이트하는 모의 개체로 바꿔 테스트 중인 개체의 동작을 격리할 수 있습니다. 이렇게 하면 웹 서비스 또는 데이터베이스와 같은 다루기 힘든 리소스 없이 단위 테스트를 실행할 수 있습니다.

MVVM 애플리케이션에서 모델 및 뷰 모델을 테스트하는 것은 다른 클래스를 테스트하는 것과 동일하며 동일한 도구와 기술을 사용할 수 있습니다.