Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
Az Áttekintésben leírtak szerint alapvető döntést kell hoznia, hogy a tesztek az éles adatbázisrendszert is magukban foglalják - ahogyan az alkalmazás teszi -, vagy a tesztek egy dupla teszten futnak majd, amely felváltja az éles adatbázisrendszert.
Egy valódi külső erőforrás tesztelése – ahelyett, hogy dupla teszttel helyettesítenél – a következő nehézségekkel járhat:
- Sok esetben egyszerűen nem lehetséges vagy praktikus tesztelni a tényleges külső erőforrást. Előfordulhat például, hogy az alkalmazás olyan szolgáltatásokkal kommunikál, amelyeken nem lehet könnyen tesztelni (a sebességkorlátozás vagy a tesztelési környezet hiánya miatt).
- Még akkor is, ha lehetséges a valós külső erőforrás bevonása, ez rendkívül lassú lehet: ha nagy mennyiségű tesztet futtat egy felhőszolgáltatáson, a tesztek túl sokáig tarthatnak. A tesztelésnek a fejlesztői mindennapi munkafolyamat részét kell képeznie, ezért fontos, hogy a tesztek gyorsan fussanak.
- A külső erőforrásokon végzett tesztek elkülönítési problémákat okozhatnak, amikor a tesztek zavarják egymást. Egy adatbázison párhuzamosan futó több teszt például módosíthatja az adatokat, és egymás meghibásodását okozhatja különböző módokon. A kettős teszt használata elkerüli ezt, mivel minden teszt saját, memórián belüli erőforráson fut, ezért természetesen elkülönítve van a többi teszttől.
A kettős teszten áteső tesztek azonban nem garantálják, hogy a program működik a valós külső erőforráson való futtatáskor. Az adatbázis-teszt kettőse például kis- és nagybetűket megkülönböztető sztring-összehasonlításokat hajthat végre, míg az éles adatbázisrendszer nem tesz különbséget a kis- és nagybetűk között. Ezek a problémák csak akkor merülnek fel, ha a teszteket a valós éles adatbázison hajtják végre, így ezek a tesztek a tesztelési stratégia fontos részét képezik.
Az adatbázis tesztelése egyszerűbb lehet, mint amilyennek látszik
A valós adatbázisokon való tesztelés fenti nehézségei miatt a fejlesztőket gyakran arra kérik, hogy először a teszteket használják, és rendelkezzenek egy robusztus tesztcsomaggal, amelyet gyakran futtathatnak a gépükön; ezzel szemben az adatbázist érintő teszteket sokkal ritkábban kell végrehajtani, és sok esetben sokkal kevesebb lefedettséget is biztosítanak. Javasoljuk, hogy az utóbbit jobban gondolja át, és azt javasoljuk, hogy az adatbázisokra valójában sokkal kevésbé legyenek hatással a fenti problémák, mint ahogy azt az emberek általában gondolják:
- A legtöbb adatbázis manapság könnyen telepíthető a fejlesztő gépére. Az olyan tárolóalapú technológiák, mint a Docker, nagyon egyszerűvé tehetik ezt, és az olyan kódtárak, mint a Testcontainers , segíthetnek automatizálni a tárolóalapú adatbázisok életciklusát a tesztekben. Az olyan technológiák, mint a GitHub-munkaterületek és a Dev Container , beállítják a teljes fejlesztési környezetet (beleértve az adatbázist is). SQL Server használata esetén tesztelhet a LocalDB-vel Windowson, vagy egyszerűen beállíthat egy Docker-rendszerképet Linuxon.
- A helyi adatbázison végzett tesztelés – ésszerű tesztadatkészlettel – általában rendkívül gyors: a kommunikáció teljesen helyi, a tesztadatok pedig általában pufferelve lesznek a memóriában az adatbázis oldalán. Maga az EF Core több mint 30 000 tesztet tartalmaz egyedül az SQL Serveren; ezek néhány perc alatt megbízhatóan befejeződnek, ci-ben hajtják végre minden egyes véglegesítésen, és a fejlesztők gyakran helyileg hajtják végre őket. Egyes fejlesztők egy memórián belüli adatbázishoz ("hamis") fordulnak abban a hitben, hogy ez a sebességhez szükséges - ez szinte soha nem így van.
- Az elkülönítés valóban akadályt jelent a tesztek valós adatbázison való futtatásakor, mivel a tesztek módosíthatják az adatokat, és zavarhatják egymást. Az adatbázis-tesztelési forgatókönyvekben azonban különböző technikákat alkalmaznak az elkülönítésre; ezekre összpontosítunk az éles adatbázisrendszer tesztelésében).
A fentiek nem arra szolgálnak, hogy becsméreljék a tesztdublőröket, vagy az ellen érveljenek, hogy használják őket. Bizonyos forgatókönyvek esetében szükség van tesztmásolatokra, amelyek másként nem tesztelhetők, például az adatbázis meghibásodásának szimulálására. Tapasztalataink szerint azonban a felhasználók gyakran elriasztják magukat attól, hogy a fenti okokból teszteljék az adatbázisukat, és úgy vélik, hogy lassú, kemény vagy megbízhatatlan, ha nem feltétlenül ez a helyzet. Az éles adatbázisrendszeren végzett tesztelés célja ennek kezelése, amely útmutatást és mintákat biztosít az adatbázis gyors, izolált tesztjeinek írásához.
Különböző típusú tesztduplikátumok
A teszt dublőrök egy átfogó kifejezés, amely nagyon különböző megközelítéseket foglal magában. Ez a szakasz az EF Core-alkalmazások teszteléséhez használt tesztkettőkkel kapcsolatos néhány gyakori technikát ismerteti:
- Használja az SQLite-ot (memóriában futó módban) hamis adatbázisként az éles adatbázisrendszer helyett.
- Használja az EF Core memóriabeli szolgáltatóját, mint adatbázis-szimulációt, amely lecseréli az éles adatbázisrendszert.
- Mockold vagy adj csonkot a
DbContextés aDbSet. - Vezessen be egy adattárréteget az EF Core és az alkalmazáskód között, és szimulálja vagy csonkolja ezt a réteget.
Az alábbiakban bemutatjuk, mit jelentenek az egyes metódusok, és összehasonlítjuk a többiekkel. Javasoljuk, hogy olvassa át a különböző módszereket, hogy teljes mértékben megértse az egyeseket. Ha úgy döntött, hogy olyan teszteket ír, amelyek nem foglalják magukban a production adatbázisrendszert, akkor az adattárréteg az egyetlen megoldás, amely lehetővé teszi az adatréteg átfogó és megbízható felülírását/mimitálását. Ez a megközelítés azonban jelentős költségekkel jár a megvalósítás és a karbantartás szempontjából.
SQLite mint adatbázis hamis
Az egyik lehetséges tesztelési módszer a produkciós adatbázis (például SQL Server) cseréje SQLite-ra, ezzel hatékonyan tesztelési „hamisítványként” használva azt. A könnyű beállítás mellett az SQLite rendelkezik egy memórián belüli adatbázis-funkcióval , amely különösen hasznos a teszteléshez: minden teszt természetesen elkülönítve van a saját memórián belüli adatbázisában, és nincs szükség tényleges fájlok felügyeletére.
Mielőtt azonban ezt tenné, fontos tisztában lenni azzal, hogy az EF Core-ban a különböző adatbázis-szolgáltatók másképp viselkednek – az EF Core nem próbálja absztrakcióra bírni az alapul szolgáló adatbázisrendszer minden aspektusát. Ez alapvetően azt jelenti, hogy az SQLite-alapú tesztelés nem garantálja ugyanazokat az eredményeket, mint az SQL Server vagy bármely más adatbázis esetén. Íme néhány példa a lehetséges viselkedésbeli különbségekre:
- Ugyanez a LINQ-lekérdezés eltérő eredményeket adhat vissza a különböző szolgáltatókon. Az SQL Server például alapértelmezés szerint nem tesz különbséget a kis- és nagybetűk között, míg az SQLite megkülönbözteti a kis- és nagybetűket. Ez azt eredményezheti, hogy a tesztek sikeresen lefutnak SQLite alatt, ahol SQL Serveren kudarcot vallanának (vagy fordítva).
- Az SQL Serveren működő egyes lekérdezések egyszerűen nem támogatottak az SQLite-en, mivel a két adatbázis pontos SQL-támogatása eltérő.
- Ha a lekérdezés szolgáltatóspecifikus metódust használ, például az SQL Serverét
EF.Functions.DateDiffDay, a lekérdezés sikertelen lesz az SQLite-en, és nem tesztelhető. - A nyers SQL működni fog, vagy meghiúsulhat, vagy eltérő eredményeket ad vissza, attól függően, hogy pontosan mit végez. Az SQL-dialektusok sokféleképpen különböznek az adatbázisokban.
Az éles adatbázisrendszeren végzett tesztekhez képest viszonylag könnyű az SQLite használatbavétele, ezért sok felhasználó ezt az utat választja. Sajnos a fenti korlátozások idővel problémássá válnak az EF Core-alkalmazások tesztelése során, még akkor is, ha úgy tűnik, hogy nem az elején vannak. Ennek eredményeképpen javasoljuk, hogy vagy a valós adatbázissal írja meg a teszteket, vagy ha a tesztkettőző használata feltétlenül szükséges, a tárolóminta költségeit vegye figyelembe az alábbiakban leírtak szerint.
Az SQLite teszteléshez való használatáról ebben a szakaszban olvashat bővebben.
Memóriaalapú, mint szimulált adatbázis
Az SQLite alternatívájaként az EF Core memórián belüli szolgáltatóval is rendelkezik. Bár ezt a szolgáltatót eredetileg az EF Core belső tesztelésének támogatására tervezték, egyes fejlesztők hamis adatbázisként használják az EF Core-alkalmazások tesztelése során. Ez erősen elriasztja: mivel egy adatbázis hamis, a memóriában ugyanazok a problémák merülnek fel, mint az SQLite (lásd fent), de emellett a következő további korlátozásokra is vonatkozik:
- A memóriabeli szolgáltató általában kevesebb lekérdezéstípust támogat, mint az SQLite-szolgáltató, mivel nem relációs adatbázis. Az éles adatbázishoz képest több lekérdezés valószínűleg meghiúsulhat vagy eltérő módon működhet.
- A tranzakciók nem támogatottak.
- A nyers SQL teljesen nem támogatott. Hasonlítsa össze ezt az SQLite-vel, ahol a nyers SQL használható, ha az SQL ugyanúgy működik az SQLite-en és az éles adatbázison.
- A memóriabeli szolgáltató nem lett optimalizálva a teljesítményre, és általában lassabban fog működni, mint az SQLite memóriabeli módban (vagy akár az éles adatbázisrendszerben).
Összefoglalva, a memóriában történő feldolgozásnak az SQLite minden hátránya, valamint néhány további hátránya is megvan, és nem kínál előnyöket cserébe. Ha egy egyszerű, memórián belüli adatbázist keres, használja az SQLite-et a memóriabeli szolgáltató helyett; de érdemes inkább az adattármintát használni az alábbiak szerint.
A memórián belüli tesztelésről ebben a szakaszban olvashat bővebben.
A DbContext és a DbSet kimásolása vagy csonkolása
Ez a megközelítés általában egy mock-keretrendszer használatával hoz létre tesztkettősöket DbContext és DbSet számára, valamint teszteli ezeket a kettősöket. A mockolás DbContext jó módszer lehet a különböző nem lekérdezési funkciók, például a Add vagy SaveChanges() hívások tesztelésére, lehetővé téve, hogy ellenőrizze, hogy a kód írási forgatókönyvekben hívta-e azokat.
A DbSet funkciók megfelelő szimulálása azonban nem lehetséges, mivel a lekérdezések LINQ-operátorokkal vannak kifejezve, amelyek statikus bővítménymetódus-hívásokIQueryable. Ennek eredményeképpen, amikor egyesek a "gúnyolásról DbSet" beszélnek, valójában azt jelentik, hogy létrehoznak egy DbSet memórián belüli gyűjtemény által létrehozott háttérrendszert, majd kiértékelik a lekérdezési operátorokat a memóriában lévő gyűjtemény alapján, akárcsak egy egyszerű IEnumerable. Ahelyett, hogy egy makett, ez valójában egyfajta hamis, ahol a memóriabeli gyűjtemény helyettesíti a valódi adatbázist.
Mivel a rendszer csak DbSet magát hamisította, és a lekérdezést a memóriában értékeli ki, ez a módszer nagyon hasonlít az EF Core memóriabeli szolgáltatóhoz: mindkét módszer a .NET-ben hajt végre lekérdezési operátorokat egy memórián belüli gyűjteményen keresztül. Ennek eredményeképpen ez a technika ugyanazokkal a hátrányokkal is jár: a lekérdezések másképp fognak viselkedni (például a kis- és nagybetűk érzékenysége körül), vagy egyszerűen meghiúsulnak (például szolgáltatóspecifikus módszerek miatt), a nyers SQL nem fog működni, és a tranzakciókat a legjobb esetben figyelmen kívül hagyják. Ennek eredményeképpen általában kerülni kell ezt a technikát a lekérdezési kódok teszteléséhez.
Adattárminta
A fenti megközelítések megpróbálták felcserélni az EF Core éles adatbázis-szolgáltatóját egy hamis tesztelési szolgáltatóval, vagy egy memórián belüli gyűjtemény által támogatott DbSet létrehozásával. Ezek a technikák hasonlóak ahhoz, hogy még mindig kiértékelik a program LINQ-lekérdezéseit - akár SQLite-ben, akár a memóriában -, és ez végső soron a fent vázolt nehézségek forrása: egy adott éles adatbázison való végrehajtásra tervezett lekérdezések nem hajthatók végre megbízhatóan máshol probléma nélkül.
A megfelelő, megbízható teszt duplázása érdekében érdemes lehet bevezetni egy adattárréteget , amely közvetít az alkalmazáskód és az EF Core között. Az adattár éles implementációja tartalmazza a tényleges LINQ-lekérdezéseket, és végrehajtja őket az EF Core-on keresztül. A tesztelés során az adattár absztrakciója közvetlenül csonkolt vagy kicsúsztatott anélkül, hogy tényleges LINQ-lekérdezésekre volna szükség, így az EF Core teljes mértékben eltávolítható a tesztelési veremből, és lehetővé teszi, hogy a tesztek kizárólag az alkalmazáskódra összpontosítsanak.
Az alábbi diagram összehasonlítja az adatbázis hamis megközelítését (SQLite/in-memory) az adattár mintájával:
Mivel a LINQ-lekérdezések már nem részei a tesztelésnek, közvetlenül is megadhat lekérdezési eredményeket az alkalmazásnak. Tegyük fel, hogy az előző megközelítések nagyjából lehetővé teszik a lekérdezési bemenetek csonkolását (például az SQL Server-táblák memórián belülire cserélését), de a tényleges lekérdezési operátorokat továbbra is végrehajtja a memóriában. Ezzel szemben a tárolóminta lehetővé teszi a lekérdezési kimenetek közvetlen imitálását, így sokkal erőteljesebb és fókuszáltabb egységtesztelést tesz lehetővé. Vegye figyelembe, hogy ennek működéséhez az adattár nem tud IQueryable-visszatérési metódusokat elérhetővé tenni, mivel ezek ismételten nem vonhatók ki; Ehelyett az IEnumerable függvényt kell visszaadni.
Mivel azonban az adattármintához minden (tesztelhető) LINQ-lekérdezést be kell ágyazni egy IEnumerable-returning metódusban, további architekturális réteget kell létrehoznia az alkalmazáshoz, és jelentős költséggel jár a megvalósítás és a karbantartás. Ezt a költséget nem szabad diszkontálni, amikor az alkalmazás tesztelésének módjáról dönt, különösen azért, mert a valós adatbázison végzett tesztekre továbbra is valószínűleg szükség lesz az adattár által közzétett lekérdezésekhez.
Érdemes megjegyezni, hogy az adattáraknak a tesztelésen kívül is vannak előnyei. Biztosítják, hogy az összes adatelérési kód egy helyen legyen koncentrálva, nem pedig az alkalmazás körül, és ha az alkalmazásnak több adatbázist kell támogatnia, akkor az adattár absztrakciója nagyon hasznos lehet a lekérdezések szolgáltatók közötti finomhangolásához.
Az adattárral végzett tesztelést bemutató mintát ebben a szakaszban találja.
Általános összehasonlítás
Az alábbi táblázat gyors, összehasonlító áttekintést nyújt a különböző tesztelési technikákról, és bemutatja, hogy mely funkciók tesztelhetők a következő megközelítés szerint:
| Tulajdonság | Memóriabeli | SQLite a memóriában | Mock DbContext | Adattárminta | Tesztelés az adatbázison |
|---|---|---|---|---|---|
| Kettős típus tesztelése | Hamis | Hamisítvány | Hamis | Hamisítvány/helyettesítő | Valós, nincs kettőzés |
| Nyers SQL? | Nem | Attól függ. | Nem | Igen | Igen |
| Tranzakciók? | Nem (figyelmen kívül hagyva) | Igen | Igen | Igen | Igen |
| Szolgáltatóspecifikus fordítások? | Nem | Nem | Nem | Igen | Igen |
| Pontos lekérdezési viselkedés? | Attól függ. | Attól függ. | Attól függ. | Igen | Igen |
| Használhatja a LINQ-t bárhol az alkalmazásban? | Igen | Igen | Igen | Nem* | Igen |
* Az összes olyan LINQ-lekérdezést, amely adatbázison hajtható végre, be kell ágyazni az IEnumerable típusú értékkel visszatérő adattári metódusokba, hogy azok mockolhatóak/csonkolhatóak legyenek.
Összefoglalás
- Javasoljuk, hogy a fejlesztők teljes tesztlefedettséggel rendelkezzenek az alkalmazásukon, miközben az a tényleges éles adatbázis-rendszerükön fut. Ez biztosítja a megbízhatóságot, hogy az alkalmazás ténylegesen éles környezetben működik, és a megfelelő kialakítással a tesztek megbízhatóan és gyorsan végrehajthatók. Mivel ezekre a tesztekre minden esetben szükség van, érdemes ott elkezdeni, és ha szükséges, később további teszteket hozzáadni tesztdublőrök használatával.
- Ha úgy döntött, hogy teszt dublőrt használ, javasoljuk, hogy implementálja a repository mintát, amely lehetővé teszi az adatelérési réteg kitrükközését az EF Core felett, ahelyett, hogy hamis EF Core-szolgáltatót (Sqlite/in-memory) vagy a
DbSetszimulálását használna. - Ha az adattár minta valamilyen okból nem járható út, fontolja meg az SQLite memóriabeli adatbázisok használatát.
- Kerülje a memóriabeli szolgáltatót tesztelési célokra – ez nem ajánlott, és csak az örökölt alkalmazások esetében támogatott.
- Ne gúnyoljon
DbSetlekérdezési célokra.