Testování částí
Tip
Tento obsah je výňatek z elektronické knihy, vzory podnikových aplikací pomocí .NET MAUI, dostupné na .NET Docs nebo jako zdarma ke stažení PDF, které lze číst offline.
U aplikací s více platformami dochází k problémům podobným desktopovým i webovým aplikacím. Mobilní uživatelé se budou lišit podle svých zařízení, síťového připojení, dostupnosti služeb a různých dalších faktorů. Aplikace pro více platforem by proto měly být testovány tak, jak by se používaly ve skutečném světě ke zlepšení kvality, spolehlivosti a výkonu. V aplikaci by se mělo provádět mnoho typů testování, včetně testování částí, testování integrace a testování uživatelského rozhraní. Testování jednotek je nejběžnější forma a základní pro vytváření vysoce kvalitních aplikací.
Test jednotek přijímá malou jednotku aplikace, obvykle metodu, izoluje ji od zbytku kódu a ověřuje, že se chová podle očekávání. Jejím cílem je zkontrolovat, že každá jednotka funkcí funguje podle očekávání, takže chyby se nešířijí v celé aplikaci. Zjištění chyby, ke které dochází, je efektivnější než pozorování účinku chyby nepřímo v sekundárním bodě selhání.
Testování částí má největší vliv na kvalitu kódu, pokud je nedílnou součástí pracovního postupu vývoje softwaru. Testy jednotek můžou fungovat jako dokumentace k návrhu a funkční specifikace pro aplikaci. Jakmile je metoda zapsána, testy jednotek by měly být zapsány, které ověřují chování metody v reakci na standardní, hraniční a nesprávné případy vstupních dat a zkontrolují explicitní nebo implicitní předpoklady provedené kódem. Alternativně při vývoji řízeném testem se testy jednotek zapisují před kódem. Další informace o vývoji řízeném testy a o tom, jak ho implementovat, najdete v tématu Návod: Vývoj řízený testy pomocí Průzkumníka testů.
Poznámka:
Testy jednotek jsou velmi účinné proti regresi. To znamená, že funkce, které fungovaly, ale byly narušeny chybnou aktualizací.
Testy jednotek obvykle používají model arrange-act-assert:
Krok | Popis |
---|---|
Uspořádat | Inicializuje objekty a nastaví hodnotu dat, která jsou předána metodě v rámci testu. |
Jednat | Vyvolá metodu v rámci testu s požadovanými argumenty. |
Assert | Ověřuje, že se akce metody v rámci testu chová podle očekávání. |
Tento model zajišťuje, že testy jednotek jsou čitelné, samopopisující a konzistentní.
Injektáž závislostí a testování jednotek
Jednou z motivací k přijetí volně propojených architektur je, že usnadňuje testování jednotek. Jedním z typů registrovaných ve službě injektáž závislostí je IAppEnvironmentService
rozhraní. Následující příklad kódu ukazuje osnovu této třídy:
public class OrderDetailViewModel : ViewModelBase
{
private IAppEnvironmentService _appEnvironmentService;
public OrderDetailViewModel(
IAppEnvironmentService appEnvironmentService,
IDialogService dialogService, INavigationService navigationService, ISettingsService settingsService)
: base(dialogService, navigationService, settingsService)
{
_appEnvironmentService = appEnvironmentService;
}
}
Třída OrderDetailViewModel
má závislost na IAppEnvironmentService
typu, který kontejner injektáž závislostí přeloží při vytvoření instance objektu OrderDetailViewModel
. Místo vytvoření objektu IAppEnvironmentService
, který využívá skutečné servery, zařízení a konfigurace k testování OrderDetailViewModel
třídy, místo toho nahraďte IAppEnvironmentService
objekt napodobeným objektem pro účely testů. Napodobený objekt je objekt, který má stejný podpis objektu nebo rozhraní, ale je vytvořen určitým způsobem, který pomáhá s testováním jednotek. Často se používá s injektáží závislostí k poskytování konkrétních implementací rozhraní pro testování různých scénářů dat a pracovních postupů.
Tento přístup umožňuje IAppEnvironmentService
předání objektu OrderDetailViewModel
do třídy za běhu a v zájmu testovatelnosti umožňuje předání OrderDetailViewModel
do třídy v testovacím čase do třídy. Hlavní výhodou tohoto přístupu je, že umožňuje provádět testy jednotek, aniž by vyžadovaly nepraktné prostředky, jako jsou funkce platformy runtime, webové služby nebo databáze.
Testování aplikací MVVM
Testování modelů a zobrazení modelů z aplikací MVVM je stejné jako testování jakékoli jiné třídy a používá stejné nástroje a techniky; to zahrnuje funkce, jako je testování jednotek a napodobování. Některé vzory, které jsou typické pro modelování a zobrazení tříd modelů, však mohou těžit z konkrétních technik testování jednotek.
Tip
Otestujte jednu věc s každým testem jednotek. S rozšířením složitosti testu je ověření tohoto testu obtížnější. Omezením testu jednotek na jeden problém můžeme zajistit, aby naše testy byly opakovatelnější, izolované a měly menší dobu provádění. Další osvědčené postupy pro testování částí s .NET najdete v tématu Osvědčené postupy.
Nenechte se pokoušet, aby cvičení testu jednotek bylo více než jeden aspekt chování jednotky. To vede k testům, které se obtížně čtou a aktualizují. Může také vést k nejasnostem při interpretaci selhání.
Multiformní aplikace eShop používá msTest k testování jednotek, která podporuje dva různé typy testů jednotek:
Typ testování | Atribut | Popis |
---|---|---|
TestMethod | TestMethod |
Definuje skutečnou testovací metodu, která se má spustit. |
DataSource | DataSource |
Testy, které platí pouze pro konkrétní sadu dat. |
Testy jednotek, které jsou součástí multiformní aplikace eShop, jsou TestMethod, takže každá metoda testování jednotek je zdobena atributem TestMethod
. Kromě MSTest existuje několik dalších testovacích architektur, včetně NUnit a xUnit.
Testování asynchronních funkcí
Při implementaci modelu MVVM modely zobrazení obvykle vyvolávají operace ve službách, často asynchronně. Testy kódu, který tyto operace vyvolá, obvykle používají napodobení jako nahrazení skutečných služeb. Následující příklad kódu ukazuje testování asynchronní funkce předáním napodobené služby do modelu zobrazení:
[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);
}
Tento test jednotek zkontroluje, že Order
vlastnost OrderDetailViewModel
instance bude mít hodnotu po InitializeAsync
vyvolání metody. Metoda InitializeAsync
je vyvolána při přechodu na odpovídající zobrazení modelu zobrazení. Další informace o navigaci naleznete v tématu Navigace.
OrderDetailViewModel
Když je instance vytvořena, očekáváIOrderService
, že instance bude zadána jako argument. Načítá OrderService
ale data z webové služby. OrderMockService
Proto instance, napodobení verze OrderService
třídy, je určen jako argument konstruktoruOrderDetailViewModel
. Potom se načtou napodobená data místo komunikace s webovou službou při vyvolání metody modelu InitializeAsync
zobrazení, která používá IOrderService
operace.
Testování implementací INotifyPropertyChanged
INotifyPropertyChanged
Implementace rozhraní umožňuje zobrazení reagovat na změny, které pocházejí ze zobrazení modelů a modelů. Tyto změny nejsou omezeny na data zobrazená v ovládacích prvcích – slouží také k řízení zobrazení, jako jsou stavy modelu zobrazení, které způsobují spuštění animací nebo zakázání ovládacích prvků.
Vlastnosti, které lze aktualizovat přímo testem jednotek, lze testovat připojením obslužné rutiny události k PropertyChanged
události a kontrolou, zda je událost vyvolána po nastavení nové hodnoty vlastnosti. Následující příklad kódu ukazuje takový test:
[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);
}
Tento test jednotek vyvolá InitializeAsync
metodu OrderViewModel
třídy, která způsobí aktualizaci jeho Order
vlastnosti. Test jednotek projde za předpokladu PropertyChanged
, že je událost vyvolána Order
pro vlastnost.
Testování komunikace založené na zprávách
Prohlédněte si modely, které používají MessagingCenter
třídu ke komunikaci mezi volně propojenými třídami, je možné testovat tak, že se přihlásíte k odběru zprávy, kterou odešle testovaný kód, jak je znázorněno v následujícím příkladu kódu:
[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);
}
Tento test jednotek zkontroluje, že CatalogViewModel
publikuje AddProduct
zprávu v reakci na spuštění AddCatalogItemCommand
. Vzhledem k tomu, že MessagingCenter
třída podporuje odběry zpráv vícesměrového vysílání, test jednotek se může přihlásit k odběru AddProduct
zprávy a spustit delegáta zpětného volání v reakci na jeho přijetí. Tento delegát zpětného volání zadaný jako výraz lambda nastaví logické pole používané příkazem Assert
k ověření chování testu.
Testování zpracování výjimek
Testy jednotek lze také zapsat, které kontrolují, že konkrétní výjimky jsou vyvolány pro neplatné akce nebo vstupy, jak je znázorněno v následujícím příkladu kódu:
[TestMethod]
public void InvalidEventNameShouldThrowArgumentExceptionText()
{
var behavior = new MockEventToCommandBehavior
{
EventName = "OnItemTapped"
};
var listView = new ListView();
Assert.Throws<ArgumentException>(() => listView.Behaviors.Add(behavior));
}
Tento test jednotek vyvolá výjimku, protože ListView
ovládací prvek nemá událost s názvem OnItemTapped
. Metoda Assert.Throws<T>
je obecná metoda, kde T
je typ očekávané výjimky. Argument předaný Assert.Throws<T>
metodě je výraz lambda, který vyvolá výjimku. Proto bude test jednotek úspěšný za předpokladu, že výraz lambda vyvolá ArgumentException
.
Tip
Vyhněte se psaní testů jednotek, které prověřují řetězce zpráv o výjimce. Řetězce zpráv o výjimkách se můžou v průběhu času měnit, takže testy jednotek, které spoléhají na jejich přítomnost, se považují za křehké.
Testování ověřování
Existují dva aspekty testování implementace ověřování: testování, že všechna ověřovací pravidla jsou správně implementována a testuje, že ValidatableObject<T>
třída funguje podle očekávání.
Logika ověřování je obvykle jednoduchá k testování, protože se obvykle jedná o samostatný proces, ve kterém výstup závisí na vstupu. Měly by existovat testy na výsledcích vyvolání Validate
metody pro každou vlastnost, která má alespoň jedno přidružené ověřovací pravidlo, jak je znázorněno v následujícím příkladu kódu:
[TestMethod]
public void CheckValidationPassesWhenBothPropertiesHaveDataTest()
{
var mockViewModel = new MockViewModel();
mockViewModel.Forename.Value = "John";
mockViewModel.Surname.Value = "Smith";
var isValid = mockViewModel.Validate();
Assert.IsTrue(isValid);
}
Tento test jednotek zkontroluje, že ověření proběhne úspěšně, pokud obě ValidatableObject<T>
vlastnosti v MockViewModel
instanci mají data.
Kromě kontroly úspěšného ověření by testy ověřovacích jednotek měly také zkontrolovat hodnoty Value
, IsValid
a Errors
vlastnost každé ValidatableObject<T>
instance, a ověřit, že třída funguje podle očekávání. Následující příklad kódu ukazuje test jednotek, který to dělá:
[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);
}
Tento test jednotek zkontroluje, že ověření selže, pokud Surname
vlastnost neobsahuje žádná data, a IsValid
Value
, a vlastnost každé Errors
ValidatableObject<T>
instance jsou správně nastavenyMockViewModel
.
Shrnutí
Test jednotek přijímá malou jednotku aplikace, obvykle metodu, izoluje ji od zbytku kódu a ověřuje, že se chová podle očekávání. Jejím cílem je zkontrolovat, že každá jednotka funkcí funguje podle očekávání, takže chyby se nešířijí v celé aplikaci.
Chování objektu v testu lze izolovat nahrazením závislých objektů napodobením objektů, které simulují chování závislých objektů. To umožňuje provádět testy jednotek, aniž by se vyžadovaly nepraktné prostředky, jako jsou funkce platformy runtime, webové služby nebo databáze.
Testování modelů a zobrazení modelů z aplikací MVVM je stejné jako testování všech ostatních tříd a stejné nástroje a techniky lze použít.