Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Jak je popsáno v přehledu, základním rozhodnutím, které je potřeba provést, je to, jestli vaše testy budou zahrnovat produkční databázový systém – stejně jako vaše aplikace – nebo jestli se testy budou spouštět proti testovacímu double, což nahrazuje produkční databázový systém.
Testování skutečného externího prostředku – místo nahrazení testovacím dvojitým testem – může zahrnovat následující potíže:
- V mnoha případech není jednoduše možné ani praktické testovat skutečné externí zdroje. Vaše aplikace může například interagovat s některými službami, které nelze snadno testovat (kvůli omezování rychlosti nebo nedostatku testovacího prostředí).
- I když je možné zahrnout skutečný externí prostředek, může to být mimořádně pomalé: spuštění velkého množství testů u cloudové služby může způsobit příliš dlouhou dobu testů. Testování by mělo být součástí každodenního pracovního postupu vývojáře, takže je důležité, aby testy běžely rychle.
- Provádění testů na externím prostředku může zahrnovat problémy s izolací, kdy testy vzájemně kolidují. Například několik testů spuštěných paralelně s databází může upravovat data a způsobit, že se navzájem nezdaří různými způsoby. Použití testovacího dvojníka se tomu vyhne, protože každý test běží na vlastním prostředku v paměti, a proto je přirozeně izolován od ostatních testů.
Testy, které projdou testovacím dvojitým testem, ale nezaručí, že váš program funguje při spuštění proti skutečnému externímu prostředku. Například testovací dvojník databáze může provádět porovnání řetězců s rozlišováním velkých a malých písmen, zatímco produkční databáze nerozlišuje malá a velká písmena. Tyto problémy jsou odhaleny pouze v případě, že se testy spouštějí v reálné produkční databázi, takže tyto testy jsou důležitou součástí jakékoli testovací strategie.
Testování databáze může být jednodušší, než se zdá
Vzhledem k výše uvedeným potížím s testováním skutečné databáze jsou vývojáři často vyzváni, aby nejdříve použili testovací dvojité testy a měli robustní sadu testů, kterou můžou často spouštět na svých počítačích; naproti tomu testy zahrnující databázi by měly být prováděny mnohem méně často, a v mnoha případech také poskytují mnohem méně pokrytí. Doporučujeme, abyste se nad tím více přemýšleli, a doporučujeme, aby databáze mohly být ve skutečnosti mnohem méně ovlivněné výše uvedenými problémy, než si lidé obvykle myslí:
- Většina databází je dnes snadno nainstalovaná na počítači vývojáře. Technologie založené na kontejnerech, jako je Docker, můžou toto zjednodušit a knihovny jako Testcontainers můžou pomoct automatizovat životní cyklus kontejnerizovaných databází v testech. Technologie, jako jsou pracovní prostory GitHubu a vývojový kontejner , nastaví pro vás celé vývojové prostředí (včetně databáze). Při použití SQL Serveru je také možné otestovat místní databázi ve Windows nebo snadno nastavit image Dockeru v Linuxu.
- Testování s místní databází – s rozumnou testovací datovou sadou – je obvykle extrémně rychlé: komunikace je zcela místní a testovací data se obvykle zachytá do vyrovnávací paměti na straně databáze. EF Core obsahuje více než 30 000 testů na sql Serveru samostatně; tyto operace jsou spolehlivě dokončeny během několika minut, spouští se v CI při každém potvrzení a jsou velmi často spouštěné místně vývojáři. Někteří vývojáři se obrátí na in-memory databázi (tzv. "fake") v přesvědčení, že je to nutné pro rychlost - to je téměř nikdy ve skutečnosti případ.
- Izolace je skutečně překážkou při spouštění testů proti skutečné databázi, protože testy mohou upravovat data a vzájemně kolidovat. Existují však různé techniky, jak zajistit izolaci ve scénářích testování databáze; zaměřujeme se na tyto funkce v oblasti testování v produkčním databázovém systému).
Výše uvedené není míněno jako odsuzování test doubles nebo argument proti jejich použití. Například jsou testovací doubly nezbytné pro některé scénáře, které nelze otestovat jinak, jako je simulace selhání databáze. V našem prostředí se ale uživatelé často vyhýbají testování vůči své databázi z výše uvedených důvodů, protože věří, že je to pomalé, těžké nebo nespolehlivé, pokud to nemusí nutně být případ. Cílem testování v produkčním databázovém systému je toto řešení, které poskytuje pokyny a ukázky pro psaní rychlých izolovaných testů pro vaši databázi.
Různé typy testovacích náhradníků
Dvojité testy jsou širokým termínem, který zahrnuje velmi odlišné přístupy. Tato část popisuje některé běžné techniky zahrnující dvojité testování pro testování aplikací EF Core:
- Použijte SQLite (režim v paměti) jako falešný databázový systém a nahraďte produkční databázový systém.
- Jako simulovanou databázi použijte poskytovatele pro paměťové úložiště EF Core k nahrazení produkčního databázového systému.
- Napodobte nebo nahradte
DbContextaDbSet. - Vytvořte vrstvu úložiště mezi EF Core a kódem aplikace a použijte mockování nebo stubování pro tuto vrstvu.
Níže prozkoumáme, co jednotlivé metody znamenají, a porovnáme je s ostatními. Doporučujeme si projít různé metody, abyste získali úplný přehled o jednotlivých metodách. Pokud jste se rozhodli psát testy, které nezahrnují produkční databázový systém, je jediným přístupem vrstva úložiště, která umožňuje komplexní a spolehlivé napodobování vrstvy dat. Tento přístup má však významné náklady z hlediska implementace a údržby.
SQLite jako falešná databáze
Jedním z možných testovacích přístupů je zaměnit produkční databázový systém (např. SQL Server) za SQLite a efektivně jej používat jako testovací předstírání. Kromě snadného nastavení má SQLite funkci databáze v paměti, která je zvlášť užitečná pro testování: každý test je přirozeně izolovaný ve vlastní databázi v paměti a není nutné spravovat žádné skutečné soubory.
Než to ale uděláte, je důležité si uvědomit, že v EF Core se různí poskytovatelé databází chovají jinak – EF Core se nepokoušá o abstrakci všech aspektů základního databázového systému. V zásadě to znamená, že testování proti SQLite nezaručuje stejné výsledky jako u SQL Serveru nebo jakékoli jiné databáze. Tady je několik příkladů možných rozdílů v chování:
- Stejný dotaz LINQ může vracet různé výsledky u různých poskytovatelů. SQL Server například ve výchozím nastavení nerozlišuje malá a velká písmena, zatímco SQLite rozlišuje malá a velká písmena. To může způsobit, že testy projdou při použití SQLite, kde by selhaly při použití SQL Serveru (nebo naopak).
- Některé dotazy, které fungují na SQL Serveru, nejsou na SQLite podporované, protože přesná podpora SQL v těchto dvou databázích se liší.
- Pokud váš dotaz používá metodu specifickou pro některého poskytovatele, jako je
EF.Functions.DateDiffDaySQL Server, takový dotaz selže na SQLite a nelze jej testovat. - Nezpracovaný SQL může fungovat nebo může selhat nebo vrátit různé výsledky v závislosti na tom, co se právě provádí. Dialekty SQL se v různých databázích liší mnoha způsoby.
V porovnání se spouštěním testů na produkčním databázovém systému je poměrně snadné začít používat SQLite, a proto s ním mnoho uživatelů začne. Výše uvedená omezení se bohužel při testování aplikací EF Core stávají problematickými, i když se zdá, že nejsou na začátku. V důsledku toho doporučujeme buď provádět testy na vaší skutečné databázi, nebo pokud je použití testovacího dublu absolutní nutností, zohlednit náklady návrhového vzoru úložiště, jak je popsáno níže.
Informace o tom, jak používat SQLite k testování, najdete v této části.
Paměťová (RAM) jako falešná databáze
Jako alternativu k SQLite je EF Core také dodáván s paměťovým poskytovatelem. I když byl tento poskytovatel původně navržen tak, aby podporoval interní testování samotného EF Core, někteří vývojáři ho používají jako databázi falešné při testování aplikací EF Core. Důrazně se to nedoporučuje: jako falešná databáze v paměti má stejné problémy jako SQLite (viz výše), ale kromě toho má následující další omezení:
- Zprostředkovatel v paměti obecně podporuje méně typů dotazů než zprostředkovatel SQLite, protože se nejedná o relační databázi. Další dotazy selžou nebo se budou chovat jinak než v produkční databázi.
- Transakce nejsou podporovány.
- Nezpracovaný SQL je zcela nepodporovaný. Porovnejte to s SQLite, kde je možné použít nezpracovaný SQL, pokud SQL funguje stejným způsobem na SQLite a v produkční databázi.
- Zprostředkovatel v paměti není optimalizovaný pro výkon a bude obecně fungovat pomaleji než SQLite v režimu v paměti (nebo dokonce v produkčním databázovém systému).
V souhrnu má in-memory všechny nevýhody SQLite, spolu s několika dalšími - a nenabízí žádné výhody jako protihodnotu. Pokud hledáte jednoduchou falešnou databázi v paměti, použijte SQLite místo provideru v paměti; místo toho doporučujeme zvážit použití vzoru úložiště, jak je uvedeno níže.
Informace o použití v paměti k testování najdete v této části.
Napodobování nebo napodobující testování pomocí DbContext a DbSet
Tento přístup obvykle používá simulační framework k vytvoření testovacího dvojníka pro DbContext a DbSet a testování oproti těmto dvojníkům. Napodobování DbContext může být dobrým přístupem pro testování různých funkcí, které nejsou dotazované , jako jsou volání Add nebo SaveChanges(), což vám umožní ověřit, jestli je váš kód volal ve scénářích zápisu.
Řádné mockování DbSetfunkčnosti dotazu však není možné, protože dotazy jsou vyjádřeny pomocí operátorů LINQ, což jsou volání statických metod rozšíření IQueryable. Výsledkem je, že když někteří lidé mluví o "napodobování DbSet", mají na mysli vytvoření DbSet založené na kolekci v paměti, a poté vyhodnocení dotazových operátorů pomocí této kolekce, stejně jako u jednoduchého IEnumerable. Nejde o mock, ale vlastně o jakýsi fake, kde kolekce v paměti nahrazuje skutečnou databázi.
Vzhledem k tomu, že pouze samotný DbSet je falešný a dotaz se vyhodnocuje v paměti, je tento přístup velmi podobný použití zprostředkovatele EF Core v paměti: obě techniky provádějí operátory dotazů v .NET v kolekci v paměti. V důsledku toho tato technika trpí stejnými nevýhodami: dotazy se budou chovat odlišně (např. citlivost písmen) nebo jednoduše selžou (např. kvůli metodám specifickým pro poskytovatele), nezpracovaný SQL nebude fungovat a transakce se v nejlepším případě ignorují. V důsledku toho by se tato technika měla obecně vyhnout testování jakéhokoli kódu dotazu.
Vzor úložiště
Výše uvedené přístupy se pokusily buď nahradit poskytovatele produkční databáze EF Core falešným testovacím poskytovatelem, nebo vytvořit DbSet podporovaný paměťovou kolekcí. Tyto techniky jsou podobné tomu, že stále vyhodnocují dotazy LINQ programu – buď v SQLite, nebo v paměti – a to je nakonec zdrojem výše uvedených potíží: dotaz navržený tak, aby se spustil v konkrétní produkční databázi, nemůže spolehlivě spouštět jinde bez problémů.
Pro správný a spolehlivý testovací dvojník zvažte zavedení vrstvy úložiště, která zprostředkovává komunikaci mezi kódem vaší aplikace a EF Core. Produkční implementace úložiště obsahuje skutečné dotazy LINQ a provádí je prostřednictvím EF Core. Při testování je abstrakce repozitáře přímo překrytá nebo napodobena, aniž by bylo nutné provádět skutečné dotazy LINQ, zcela odstraní EF Core z vaší testovací struktury a umožní testům soustředit se pouze na kód aplikace.
Následující diagram porovnává falešný přístup k databázi (SQLite/in-memory) se vzorem úložiště:
Vzhledem k tomu, že dotazy LINQ už nejsou součástí testování, můžete přímo poskytnout výsledky dotazů vaší aplikaci. Jinak řečeno, předchozí přístupy zhruba umožňují nahradit vstupy dotazů (např. nahradit tabulky SQL Serveru tabulkami uloženými v paměti) a následně spouštět skutečné operátory dotazů v paměti. Repozitářová vrstva naproti tomu umožňuje přímo simulovat výstupy dotazů, což umožňuje mnohem výkonnější a zaměřené jednotkové testování. Mějte na paměti, že aby to fungovalo, vaše úložiště nemůže zveřejnit žádné metody vrácení IQueryable, protože tyto metody nelze znovu imitovat; Místo toho by se měl vrátit IEnumerable.
Vzhledem k tomu, že model úložiště vyžaduje zapouzdření každého a každého (testovatelného) dotazu LINQ v metodě vrácení IEnumerable, ukládá do vaší aplikace další vrstvu architektury a může mít značné náklady na implementaci a údržbu. Tyto náklady by neměly být podceněny při výběru způsobu testování aplikace, zejména vzhledem k tomu, že testy skutečné databáze jsou stále pravděpodobně potřeba pro dotazy vystavené úložištěm.
Stojí za zmínku, že úložiště mají výhody mimo testování. Zajišťují, aby se veškerý kód přístupu k datům zaostřil na jednom místě a nešířil se po aplikaci, a pokud vaše aplikace potřebuje podporovat více než jednu databázi, může být abstrakce úložiště velmi užitečná při úpravě dotazů napříč poskytovateli.
Ukázku ukazující testování s úložištěm najdete v této části.
Celkové porovnání
Následující tabulka obsahuje rychlý, srovnávací přehled o různých technikách testování a ukazuje, které funkce je možné testovat v rámci tohoto přístupu:
| Funkce | V paměti | SQLite v paměti | Mockování DbContextu | Vzor úložiště | Testování v databázi |
|---|---|---|---|---|---|
| Test typu double | Falešný | Falešný | Falešný | Mockovací nebo zástupný modul | Reálné číslo, nikoli double |
| Nezpracovaný SQL? | Ne | Závisí | Ne | Ano | Ano |
| Transakce? | Ne (ignorováno) | Ano | Ano | Ano | Ano |
| Překlady specifické pro poskytovatele? | Ne | Ne | Ne | Ano | Ano |
| Přesné chování dotazu? | Závisí | Závisí | Závisí | Ano | Ano |
| Může linQ používat kdekoli v aplikaci? | Ano | Ano | Ano | Ne* | Ano |
* Všechny testovatelné dotazy LINQ nad databází musí být zapouzdřeny v metodách úložiště, které vracejí IEnumerable, aby mohly být simulovány či testovány.
Shrnutí
- Doporučujeme, aby vývojáři měli dobré testovací pokrytí své aplikace spuštěné ve skutečném produkčním databázovém systému. To poskytuje jistotu, že aplikace ve skutečnosti funguje v produkčním prostředí a se správným návrhem může testy spolehlivě a rychle provádět. Vzhledem k tomu, že se tyto testy vyžadují v každém případě, je vhodné začít s nimi a v případě potřeby později přidat testy s použitím testovacích dublů.
- Pokud jste se rozhodli použít testovací doubl, doporučujeme implementovat vzor úložiště, který vám umožní zastupovat nebo simulovat vrstvu přístupu k datům nad vrstvou EF Core, namísto použití falešného poskytovatele EF Core (Sqlite/in-memory) nebo napodobení
DbSet. - Pokud model úložiště z nějakého důvodu není proveditelnou možností, zvažte použití databází SQLite v paměti.
- Vyhněte se zprostředkovateli v paměti pro účely testování – nedoporučuje se a podporuje se jenom pro starší verze aplikací.
- Vyhněte se napodobování
DbSetpro účely dotazování.