Testowanie jednostkowe aplikacji dla przedsiębiorstw
Uwaga
Ta książka elektroniczna została opublikowana wiosną 2017 r. i od tego czasu nie została zaktualizowana. Jest wiele w książce, która pozostaje cenna, ale niektóre z materiałów są przestarzałe.
Aplikacje mobilne mają unikatowe problemy, które aplikacje klasyczne i internetowe nie muszą się martwić. Użytkownicy mobilni będą różnić się od urządzeń, z których korzystają, przez łączność sieciową, przez dostępność usług i szereg innych czynników. W związku z tym aplikacje mobilne powinny być testowane, ponieważ będą używane w świecie rzeczywistym w celu poprawy jakości, niezawodności i wydajności. Istnieje wiele typów testów, które należy wykonać w aplikacji, w tym testy jednostkowe, testowanie integracji i testowanie interfejsu użytkownika, a testowanie jednostkowe jest najbardziej typową formą testowania.
Test jednostkowy wykonuje małą jednostkę aplikacji, zazwyczaj metodę, izoluje ją od pozostałej części kodu i sprawdza, czy zachowuje się zgodnie z oczekiwaniami. Jego celem jest sprawdzenie, czy każda jednostka funkcjonalności działa zgodnie z oczekiwaniami, aby błędy nie propagowały się w całej aplikacji. Wykrywanie usterki, w której występuje, jest bardziej wydajne, aby zaobserwować efekt usterki pośrednio w pomocniczym punkcie awarii.
Testowanie jednostkowe ma największy wpływ na jakość kodu, gdy jest integralną częścią przepływu pracy tworzenia oprogramowania. Po zapisaniu metody należy napisać testy jednostkowe, które weryfikują zachowanie metody w odpowiedzi na standardowe, granice i nieprawidłowe przypadki danych wejściowych oraz czy sprawdzają wszelkie jawne lub niejawne założenia dokonane przez kod. Alternatywnie, w przypadku programowania opartego na testach, testy jednostkowe są zapisywane przed kodem. W tym scenariuszu testy jednostkowe działają zarówno jako dokumentacja projektowa, jak i specyfikacje funkcjonalne.
Uwaga
Testy jednostkowe są bardzo skuteczne w odniesieniu do regresji — czyli funkcje używane do pracy, ale zostały zakłócone przez wadliwą aktualizację.
Testy jednostkowe zwykle używają wzorca rozmieszczania asertywnego:
- Sekcja rozmieszczania metody testu jednostkowego inicjuje obiekty i ustawia wartość danych przekazywanych do metody testowanej.
- Sekcja act wywołuje metodę testową z wymaganymi argumentami.
- Sekcja asercji sprawdza, czy działanie metody testowej działa zgodnie z oczekiwaniami.
Zgodnie z tym wzorcem gwarantuje, że testy jednostkowe są czytelne i spójne.
Wstrzykiwanie zależności i testowanie jednostkowe
Jedną z motywacji do przyjęcia luźno powiązanej architektury jest to, że ułatwia testowanie jednostkowe. Jednym z typów zarejestrowanych w usłudze Autofac jest OrderService
klasa . Poniższy przykład kodu przedstawia konspekt tej klasy:
public class OrderDetailViewModel : ViewModelBase
{
private IOrderService _ordersService;
public OrderDetailViewModel(IOrderService ordersService)
{
_ordersService = ordersService;
}
...
}
Klasa OrderDetailViewModel
ma zależność od IOrderService
typu, który kontener rozpoznaje, gdy tworzy wystąpienie OrderDetailViewModel
obiektu. Jednak zamiast tworzyć obiekt do testowania jednostkowego OrderService
OrderDetailViewModel
klasy, zamiast tego zastąp OrderService
obiekt pozorem na potrzeby testów. Rysunek 10–1 ilustruje tę relację.
Rysunek 10–1. Klasy implementujące interfejs IOrderService
Takie podejście umożliwia OrderService
przekazanie obiektu do OrderDetailViewModel
klasy w czasie wykonywania, a w interesie możliwości testowania umożliwia OrderMockService
przekazanie klasy do OrderDetailViewModel
klasy w czasie testowania. Główną zaletą tego podejścia jest to, że umożliwia wykonywanie testów jednostkowych bez konieczności używania niezłych zasobów, takich jak usługi internetowe lub bazy danych.
Testowanie aplikacji MVVM
Testowanie modeli i wyświetlanie modeli z aplikacji MVVM jest identyczne z testowaniem innych klas, a można używać tych samych narzędzi i technik, takich jak testowanie jednostkowe i pozorowanie. Istnieją jednak pewne wzorce typowe dla klas modelu i wyświetlania modeli, które mogą korzystać z określonych technik testowania jednostkowego.
Napiwek
Przetestuj jedną rzecz przy użyciu każdego testu jednostkowego. Nie należy kusić do wykonania ćwiczenia testowego jednostkowego więcej niż jednego aspektu zachowania jednostki. W ten sposób prowadzi do testów, które są trudne do odczytania i aktualizacji. Może to również prowadzić do nieporozumień podczas interpretowania błędu.
Aplikacja mobilna eShopOnContainers wykonuje testy jednostkowe, które obsługują dwa różne typy testów jednostkowych:
- Fakty to testy, które są zawsze prawdziwe, które testują niezmienne warunki.
- Teorie to testy, które są prawdziwe tylko dla określonego zestawu danych.
Testy jednostkowe dołączone do aplikacji mobilnej eShopOnContainers są testami faktów, a więc każda metoda testu jednostkowego jest ozdobiona atrybutem [Fact]
.
Uwaga
Testy xUnit są wykonywane przez moduł uruchamiający testy. Aby wykonać moduł uruchamiający testy, uruchom projekt eShopOnContainers.TestRunner dla wymaganej platformy.
Testowanie funkcji asynchronicznych
Podczas implementowania wzorca MVVM wyświetlanie modeli zwykle wywołuje operacje na usługach, często asynchronicznie. Testy kodu, który wywołuje te operacje, zwykle używają makiety jako zamienników dla rzeczywistych usług. W poniższym przykładzie kodu pokazano testowanie funkcji asynchronicznych przez przekazanie makiety usługi do modelu widoku:
[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);
}
Ten test jednostkowy sprawdza, czy Order
właściwość OrderDetailViewModel
wystąpienia będzie miała wartość po InitializeAsync
wywołaniu metody. Metoda InitializeAsync
jest wywoływana po przejściu do odpowiedniego widoku modelu widoku. Aby uzyskać więcej informacji na temat nawigacji, zobacz Nawigacja.
Po utworzeniu OrderDetailViewModel
wystąpienia oczekuje OrderService
się, że wystąpienie zostanie określone jako argument. OrderService
Pobiera jednak dane z usługi internetowej. W związku z OrderMockService
tym wystąpienie, które jest pozorną wersją OrderService
klasy, jest określane jako argument konstruktora OrderDetailViewModel
. Następnie po wywołaniu metody modelu InitializeAsync
widoku, która wywołuje operacje IOrderService
, pobierane są pozorne dane zamiast komunikować się z usługą internetową.
Testowanie implementacji INotifyPropertyChanged
Zaimplementowanie interfejsu INotifyPropertyChanged
umożliwia widokom reagowanie na zmiany pochodzące z modeli i modeli widoku. Te zmiany nie są ograniczone do danych wyświetlanych w kontrolkach — są one również używane do kontrolowania widoku, takich jak stany modelu wyświetlania, które powodują uruchomienie animacji lub wyłączenie kontrolek.
Właściwości, które można zaktualizować bezpośrednio przez test jednostkowy, można przetestować, dołączając program obsługi zdarzeń do PropertyChanged
zdarzenia i sprawdzając, czy zdarzenie jest wywoływane po ustawieniu nowej wartości dla właściwości. Poniższy przykład kodu przedstawia taki 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);
}
Ten test jednostkowy wywołuje metodę InitializeAsync
OrderViewModel
klasy, co powoduje zaktualizowanie jej Order
właściwości. Test jednostkowy zakończy się pomyślnie, pod warunkiem, że PropertyChanged
zdarzenie zostanie zgłoszone dla Order
właściwości .
Testowanie komunikacji opartej na komunikatach
Wyświetlanie modeli używających MessagingCenter
klasy do komunikowania się między luźno powiązanymi klasami może być testowane jednostkowo przez subskrybowanie komunikatu wysyłanego przez testowany kod, jak pokazano w poniższym przykładzie kodu:
[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);
}
Ten test jednostkowy sprawdza, czy CatalogViewModel
komunikat jest publikowany AddProduct
w odpowiedzi na jego AddCatalogItemCommand
wykonanie. MessagingCenter
Ponieważ klasa obsługuje subskrypcje komunikatów multiemisji, test jednostkowy może subskrybować AddProduct
komunikat i wykonywać delegat wywołania zwrotnego w odpowiedzi na jego odbieranie. Ten delegat wywołania zwrotnego określony jako wyrażenie lambda ustawia boolean
pole używane przez Assert
instrukcję w celu zweryfikowania zachowania testu.
Testowanie obsługi wyjątków
Testy jednostkowe można również napisać, aby sprawdzić, czy określone wyjątki są zgłaszane dla nieprawidłowych akcji lub danych wejściowych, jak pokazano w poniższym przykładzie kodu:
[Fact]
public void InvalidEventNameShouldThrowArgumentExceptionText()
{
var behavior = new MockEventToCommandBehavior
{
EventName = "OnItemTapped"
};
var listView = new ListView();
Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));
}
Ten test jednostkowy zgłosi wyjątek, ponieważ kontrolka ListView
nie ma zdarzenia o nazwie OnItemTapped
. Metoda Assert.Throws<T>
jest metodą ogólną, w której T
jest typem oczekiwanego wyjątku. Argument przekazany do Assert.Throws<T>
metody jest wyrażeniem lambda, które zgłosi wyjątek. W związku z tym test jednostkowy przejdzie pod warunkiem, że wyrażenie lambda zgłasza ArgumentException
wartość .
Napiwek
Unikaj pisania testów jednostkowych, które badają ciągi komunikatów o wyjątku. Ciągi komunikatów wyjątków mogą ulec zmianie w czasie, a więc testy jednostkowe, które polegają na ich obecności, są uważane za kruche.
Sprawdzanie poprawności testowania
Istnieją dwa aspekty testowania implementacji walidacji: testowanie, czy wszystkie reguły walidacji są prawidłowo implementowane, i testowanie, czy ValidatableObject<T>
klasa działa zgodnie z oczekiwaniami.
Logika walidacji jest zwykle prosta do przetestowania, ponieważ zazwyczaj jest to proces samodzielny, w którym dane wyjściowe zależą od danych wejściowych. Powinny istnieć testy dotyczące wyników wywoływania Validate
metody dla każdej właściwości, która ma co najmniej jedną skojarzną regułę walidacji, jak pokazano w poniższym przykładzie kodu:
[Fact]
public void CheckValidationPassesWhenBothPropertiesHaveDataTest()
{
var mockViewModel = new MockViewModel();
mockViewModel.Forename.Value = "John";
mockViewModel.Surname.Value = "Smith";
bool isValid = mockViewModel.Validate();
Assert.True(isValid);
}
Ten test jednostkowy sprawdza, czy walidacja zakończy się powodzeniem, gdy obie ValidatableObject<T>
właściwości w wystąpieniu MockViewModel
mają dane.
Oprócz sprawdzania, czy weryfikacja zakończy się pomyślnie, testy jednostkowe weryfikacji powinny również sprawdzać wartości Value
właściwości , IsValid
i Errors
każdego ValidatableObject<T>
wystąpienia, aby sprawdzić, czy klasa działa zgodnie z oczekiwaniami. W poniższym przykładzie kodu pokazano test jednostkowy, który wykonuje następujące czynności:
[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);
}
Ten test jednostkowy sprawdza, czy sprawdzanie poprawności kończy się niepowodzeniem, gdy Surname
właściwość MockViewModel
obiektu nie ma żadnych danych, a Value
właściwość , IsValid
i Errors
każdego ValidatableObject<T>
wystąpienia jest poprawnie ustawiona.
Podsumowanie
Test jednostkowy wykonuje małą jednostkę aplikacji, zazwyczaj metodę, izoluje ją od pozostałej części kodu i sprawdza, czy zachowuje się zgodnie z oczekiwaniami. Jego celem jest sprawdzenie, czy każda jednostka funkcjonalności działa zgodnie z oczekiwaniami, aby błędy nie propagowały się w całej aplikacji.
Zachowanie obiektu testowanego można odizolować, zastępując obiekty zależne obiektami pozorowanymi, które symulują zachowanie obiektów zależnych. Dzięki temu testy jednostkowe mogą być wykonywane bez konieczności używania nieporętnych zasobów, takich jak usługi internetowe lub bazy danych.
Testowanie modeli i wyświetlanie modeli z aplikacji MVVM jest identyczne z testowaniem innych klas, a te same narzędzia i techniki mogą być używane.