Sdílet prostřednictvím


Volba testovací strategie

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:

  1. V mnoha případech není jednoduše možné ani praktické testovat skutečné externí prostředky. 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í).
  2. 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.
  3. 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í dvojitého testu se tomu vyhne, protože každý test běží na vlastním prostředku v paměti, a proto je přirozeně izolovaný od jiný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 dvojitý test 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ázový systém 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í:

  1. Většina databází je dnes snadno nainstalovaná na počítači vývojáře. Technologie založené na kontejnerech, jako je Docker, vám to můžou velmi snadno a technologie, jako jsou pracovní prostory GitHubu a vývojový kontejner , nastavit 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.
  2. 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 databázi v paměti ("falešný") ve přesvědčení, že to je potřeba pro rychlost - to je téměř nikdy ve skutečnosti případ.
  3. 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é údaje nejsou určeny k tomu, aby odmítaly test doubles nebo argumentovat proti jejich použití. Pro jednu věc jsou testovací dvojité testy nezbytné pro některé scénáře, které nelze testovat jinak, například 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 doubles

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:

  1. Použijte SQLite (režim v paměti) jako falešný databázový systém a nahraďte produkční databázový systém.
  2. Jako falešný databázový systém použijte zprostředkovatele EF Core v paměti a nahraďte produkční databázový systém.
  3. Napodobení nebo výsměch a DbContext DbSet.
  4. Představte si vrstvu úložiště mezi EF Core a kódem aplikace a napodobení nebo zástupným kódem této vrstvy.

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ázový

Jedním z možných testovacích přístupů je prohodit produkční databázi (e.g. SQL Server) s nástrojem SQLite a efektivně ho používat jako testovací "faleš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šují malá a velká písmena. To může zajistit, aby testy prošly proti SQLite, kde by selhaly proti 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 se váš dotaz stane, že používá metodu specifickou pro zprostředkovatele, například SQL Server, tento dotaz selže na SQLite a nelze ho EF.Functions.DateDiffDaytestovat.
  • 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ů v produkčním databázovém systému je poměrně snadné začít s nástrojem SQLite a tak mnoho uživatelů. 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ď zapsat testy do skutečné databáze, nebo pokud je použití dvojitého testu absolutní nutností, připojování nákladů na vzor úložiště, jak je popsáno níže.

Informace o tom, jak používat SQLite k testování, najdete v této části.

In-memory as a database fake

Jako alternativu k SQLite je také EF Core dodáván s poskytovatelem v paměti. 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ázový kód má v paměti 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 vracet. Pokud hledáte jednoduchý falešný databázi v paměti, použijte SQLite místo poskytovatele v paměti; ale místo toho zvažte použití vzoru úložiště, jak je popsáno níže.

Informace o použití v paměti k testování najdete v této části.

Napodobování nebo stubbing DbContext a DbSet

Tento přístup obvykle používá napodobenou architekturu k vytvoření dvojitého DbContext testu a DbSeta testování těchto dvojitých hodnot. 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.

Správné napodobování DbSet funkcí dotazu však není možné, protože dotazy jsou vyjádřeny prostřednictvím operátorů LINQ, což jsou volání metody statického rozšíření .IQueryable V důsledku toho, když někteří lidé mluví o "napodobování DbSet", to, co opravdu znamenají, je, že vytvoří zálohovanou DbSet kolekci v paměti a pak vyhodnotí operátory dotazů proti této kolekci v paměti, stejně jako jednoduchý IEnumerable. Místo napodobení je to vlastně druh falešného, kde kolekce v paměti nahradí 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ď prohodit poskytovatele produkční databáze EF Core s falešným testovacím poskytovatelem, nebo vytvořit zálohovanou DbSet kolekci v paměti. 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ý spolehlivý test dvakrát zvažte zavedení vrstvy úložiště, která mediuje 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 úložiště přímo překrytá nebo napodobena, aniž by bylo nutné provádět skutečné dotazy LINQ, efektivně odstraňuje EF Core z testovacího zásobníku a umožňuje 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ě:

Porovnání falešného poskytovatele 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í zahltit vstupy dotazů (např. nahradit tabulky SQL Serveru tabulkami v paměti), ale pak stále spouštět skutečné operátory dotazů v paměti. Model úložiště naproti tomu umožňuje přímo ztěžovat výstupy dotazů, což umožňuje mnohem výkonnější a zaměřené testování jednotek. 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 zahltit; 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 se neměly zlevnět při výběru způsobu testování aplikace, zejména v případě, že testy skutečné databáze budou 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 Napodobení DbContext Vzor úložiště Testování v databázi
Test typu double Falešný Falešný Falešný Napodobení nebo zástupný procedura Real, no double
Nezpracovaný SQL? No Závisí No Ano Yes
Transakce? Ne (ignorováno) Ano Ano Ano Yes
Překlady specifické pro poskytovatele? No No No Ano Yes
Přesné chování dotazu? Závisí Závisí Závisí Ano Yes
Může linQ používat kdekoli v aplikaci? Ano Ano Yes Ne* Ano

* Všechny testovatelné dotazy LINQ databáze musí být zapouzdřeny v metodách úložiště IEnumerable-returning, aby bylo možné zakrývat/napodobovat.

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 tam a v případě potřeby přidat testy s použitím testovacích doubles později podle potřeby.
  • Pokud jste se rozhodli použít testovací dvojitou verzi, doporučujeme implementovat vzor úložiště, který vám umožní ztěžovat nebo napodobovat vrstvu přístupu k datům nad EF Core, a ne použít 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í DbSet pro účely dotazování.