it-swarm-eu.dev

(Proč) je důležité, aby test jednotky nezkoušel závislosti?

Chápu význam automatizovaného testování a používám jej všude tam, kde je problém dostatečně přesně určen, abych mohl přijít s dobrými testovacími případy. Všiml jsem si však, že někteří lidé tady a na StackOverflow zdůrazňují testování pouze jednotku, ne její závislosti. Tady nevidím přínos.

Zesměšňování/zatahování, aby se zabránilo závislostem na testování, zvyšuje testy složitost. Přidává do vašeho produkčního kódu požadavky na umělou flexibilitu/oddělení, což podporuje zesměšňování. (Nesouhlasím s kýmkoli, kdo říká, že to podporuje dobrý design. Psaní dalšího kódu, zavedení věcí, jako jsou rámce pro závislostní injekce, nebo jiné přidávání složitosti do vaší kódové základny, aby se věci staly flexibilnějšími/pluggable/rozšiřitelné/oddělené bez skutečného použití, je nadměrné, ne dobrý design.)

Zadruhé, testování závislostí znamená, že kritický nízkoúrovňový kód, který se používá všude, se testuje s jinými vstupy, než s těmi, o nichž si kdokoli, kdo napsal jeho testy, výslovně myslel. Našel jsem spoustu chyb v nízkoúrovňové funkčnosti spuštěním testů jednotek na vysoké úrovni funkčnosti bez vysmívání nízkoúrovňové funkčnosti, na které závisí. V ideálním případě by to byly nalezeny jednotkovými testy funkčnosti na nízké úrovni, ale vždy se stávají zmeškané případy.

Jaká je druhá stránka tohoto? Je opravdu důležité, aby test jednotek nezkoušel také jeho závislosti? Pokud ano, proč?

Edit: Dokážu pochopit hodnotu zesměšňování vnějších závislostí , jako jsou databáze, sítě, webové služby atd. (Děkuji Anna Lear za motivaci, abych to objasnil .) Měl jsem na mysli vnitřní závislosti , tj. Jiné třídy, statické funkce atd., Které nemají žádné přímé externí závislosti.

107
dsimcha

Je to věc definice. Test závislostí je test integrace, nikoli test jednotky. Měli byste také mít integrační testovací sadu. Rozdíl je v tom, že integrační testovací sada může být spuštěna v jiném testovacím rámci a pravděpodobně ne jako součást sestavení, protože trvá déle.

Pro náš produkt: Naše testy jednotek probíhají s každou sestavou, která trvá několik sekund. Podskupina našich integračních testů probíhá s každou check-in, která trvá 10 minut. Naše kompletní integrační sada je spuštěna každou noc, což trvá 4 hodiny.

118
Jeffrey Faust

Testování se všemi závislostmi je stále důležité, ale je to spíš v oblasti testování integrace, jak řekl Jeffrey Faust.

Jedním z nejdůležitějších aspektů testování jednotek je zajištění důvěryhodnosti vašich testů. Pokud nevěříte, že úspěšný test opravdu znamená, že věci jsou dobré a že neúspěšný test skutečně znamená problém ve výrobním kódu, vaše testy nejsou zdaleka tak užitečné, jak mohou být.

Aby vaše testy byly důvěryhodné, musíte udělat několik věcí, ale na tuto odpověď se zaměřím pouze na jednu. Musíte se ujistit, že je lze snadno spustit, aby je všichni vývojáři mohli snadno spustit před kontrolou kódu. „Snadné spuštění“ znamená, že vaše testy běží rychle a neexistuje rozsáhlá konfigurace nebo nastavení je nutilo, aby se rozešly. V ideálním případě by měl být kdokoli schopen zkontrolovat nejnovější verzi kódu, okamžitě spustit testy a vidět, jak projdou.

Abstrakt závislosti na jiných věcech (souborový systém, databáze, webové služby atd.) Vám umožní vyhnout se požadavkům na konfiguraci a učiní vás a další vývojáře méně náchylnými k situacím, kdy jste v pokušení říci: „Ach, testy selhaly, protože já ne“ Nechám nastavit síťový podíl. No dobře. Budu je spouštět později. “

Pokud chcete vyzkoušet, co děláte s některými daty, vaše jednotkové testy pro tento obchodní logický kód by se neměly starat o jak tato data získáte. Být schopen otestovat základní logiku vaší aplikace bez závislosti na podpůrných věcech, jako jsou databáze, je úžasné. Pokud to neděláte, tak vám chybí.

P.S. Chtěl bych dodat, že je určitě možné se ve jménu testovatelnosti přepracovat. Testovací řízení vaší aplikace to pomáhá zmírnit. V každém případě však špatné implementace přístupu tento přístup nezhoršují. Všechno může být zneužito a předraženo, pokud se člověk nebude ptát „proč to dělám?“ zatímco se vyvíjí.


public class MyClass 
{
    private SomeClass someClass;
    public MyClass()
    {
        someClass = new SomeClass();
    }

    // use someClass in some way
}

Obecně mi nezáleží na tom, jak je vytvořen SomeClass. Chci to jen použít. Pokud se SomeClass změní a nyní vyžaduje parametry konstruktoru ... to není můj problém. Neměl bych měnit MyClass, aby to vyhovoval.

Nyní se to jen dotýká konstrukční části. Pokud jde o testy jednotek, chci se také chránit před ostatními třídami. Pokud testuji MyClass, ráda bych věděla, že neexistují žádné externí závislosti, že SomeClass v určitém okamžiku nezavedla připojení k databázi nebo jiné externí propojení.

Ještě větší problém však spočívá v tom, že také vím, že některé výsledky mých metod závisí na výstupu z některé metody na SomeClass. Bez vysmívání/vycpávání SomeClass bych možná neměl možnost změnit tento vstup v poptávce. Pokud budu mít štěstí, budu schopen vytvořit své prostředí uvnitř testu takovým způsobem, že to vyvolá správnou odpověď od SomeClass, ale tímto způsobem se do mých testů zavede složitost a učiní se křehkými.

Přepisování MyClass tak, aby akceptoval instanci SomeClass ve konstruktoru, mi umožňuje vytvořit falešnou instanci SomeClass, která vrací hodnotu, kterou chci (buď prostřednictvím zesměšňovacího rámce nebo pomocí ručního zesměšňování). Obvykle nemám zavést rozhraní v tomto případě. Zda to udělat nebo ne, je v mnoha ohledech osobní volba, která může být diktována vaším jazykem volby (např. Rozhraní jsou s větší pravděpodobností v C #, ale určitě byste to v Ruby nepotřebovali).

39
Adam Lear

Kromě problému s testováním jednotky vs. integrace zvažte následující.

Třída Widget má závislosti na třídách Thingamajig a WhatsIt.

Test jednotky Widget se nezdaří.

Ve které třídě je problém?

Pokud jste odpověděli „spustili debugger“ nebo „přečetli kód, dokud ho nenajdu“, chápete důležitost testování pouze jednotky, nikoli závislostí.

22
Brook

Představte si, že programování je jako vaření. Pak je testování jednotky stejné jako ujistit se, že vaše ingredience jsou čerstvé, chutné atd. Zatímco integrační testování je jako ujistit se, že vaše jídlo je chutné.

Nakonec se ujistěte, že vaše jídlo je chutné (nebo že váš systém funguje) je nejdůležitější věc, konečný cíl. Ale pokud vaše ingredience, myslím jednotky, práce, budete mít levnější způsob, jak to zjistit.

Pokud můžete zaručit, že vaše jednotky/metody fungují, je pravděpodobné, že budete mít funkční systém. Zdůrazňuji „pravděpodobnější“ než „jistý“. Stále potřebujete integrační testy, stejně jako stále potřebujete někoho, aby ochutnal jídlo, které jste uvařili, a řekl vám, že konečný produkt je dobrý. Budete mít snadnější čas se tam dostat s čerstvými ingrediencemi, to je vše.

15
Julio

Testování jednotek jejich úplnou izolací umožní otestovat VŠECHNY možné variace dat a situací, které tato jednotka může předložit. Protože je izolován od zbytku, můžete ignorovat varianty, které nemají přímý účinek na testovanou jednotku. to zase velmi sníží složitost testů.

Testování s různými úrovněmi integrovaných závislostí vám umožní testovat konkrétní scénáře, které by nemusely být testovány v testech jednotek.

Oba jsou důležité. Při provádění jednotkových testů budete mít při integraci součástí vždy složitější drobné chyby. Testování integrace znamená, že testujete systém bez jistoty, že jednotlivé části stroje nebyly testovány. Myslet si, že můžete dosáhnout lepšího úplného testu pouhým provedením integračního testování, je téměř nemožné, protože čím více komponent přidáte, počet vstupních kombinací roste VELMI rychle (přemýšlejte faktoriálně) a vytvoření testu s dostatečným pokrytím je velmi rychle nemožné.

Stručně řečeno, tři úrovně „testování jednotek“, které obvykle používám téměř ve všech svých projektech:

  • Jednotkové testy, izolované s zesměšňovanými nebo stubbed závislostmi k testování pekla jedné komponenty. V ideálním případě by se někdo pokusil o úplné pokrytí.
  • Integrační testy pro testování jemnějších chyb. Pečlivě vytvořené namátkové kontroly, které by ukázaly mezní případy a typické podmínky. Úplné pokrytí často není možné, úsilí zaměřené na to, co by způsobilo selhání systému.
  • Testování specifikací pro vstřikování různých dat ze skutečného života do systému, instrumentace dat (pokud je to možné) a sledování výstupu, aby bylo zajištěno, že vyhovuje specifikacím a obchodním pravidlům.

Injekční závislost je jedním z velmi účinných způsobů, jak toho dosáhnout, protože umožňuje velmi snadno izolovat komponenty pro testy jednotek, aniž by do systému přidávala složitost. Váš obchodní scénář nemusí zaručovat použití injekčního mechanismu, ale váš testovací scénář téměř bude. To je pro mě dostačující, aby se to stalo nezbytným. Můžete je také použít k nezávislému testování různých úrovní abstrakce pomocí testování částečné integrace.

6
Newtopian

Jaká je druhá stránka tohoto? Je opravdu důležité, aby test jednotek nezkoušel také jeho závislosti? Pokud ano, proč?

Jednotka. Prostředky singulární.

Testování 2 věcí znamená, že máte dvě věci a všechny funkční závislosti.

Pokud přidáte třetí věc, zvýšíte funkční závislosti v testech za hranicemi lineárně. Propojení mezi věcmi rostou rychleji než počet věcí.

Mezi n testovanými položkami jsou n (n-1)/2 potenciální závislosti.

To je velký důvod.

Jednoduchost má hodnotu.

4
S.Lott

Pamatuješ, jak ses poprvé naučil dělat rekurzi? Můj profesor řekl: „Předpokládejme, že máte metodu, která dělá x“ (např. Řeší fibbonacci pro jakékoli x). "Chcete-li vyřešit pro x, musíte zavolat tuto metodu pro x-1 a x-2". Ve stejném ohledu vám vytažení závislostí umožní předstírat, že existují, a otestovat, že aktuální jednotka dělá to, co by měla dělat. Předpokládáme samozřejmě, že závislost testujete stejně přísně.

To je v podstatě SRP v práci. Zaměření na jedinou zodpovědnost i za vaše testy odstraní množství duševního žonglování, které musíte udělat.

2
Michael Brown

(Toto je malá odpověď. Díky @TimWilliscroft za nápovědu.)

Chyby lze snáze lokalizovat, pokud:

  • Testovaná jednotka i každá její závislost jsou testovány nezávisle.
  • Každý test selže, a to pouze tehdy, pokud je v kódu, na který se test vztahuje, chyba.

To funguje pěkně na papíře. Jak je však znázorněno v popisu OP (závislosti jsou buggy), pokud nejsou závislosti testovány, bylo by obtížné určit umístění poruchy.

2
rwong

Hodně dobrých odpovědí. Také bych přidal několik dalších bodů:

Testování jednotek také umožňuje testovat váš kód, když neexistují závislosti . Např. vy nebo váš tým ještě nenapsali další vrstvy, nebo možná čekáte na rozhraní dodané jinou společností.

Testy jednotek také znamenají, že na vašem dev stroji nemusíte mít úplné prostředí (např. Databáze, webový server atd.). Velmi bych doporučil, aby všichni vývojáři do měli takové prostředí, ať už je to omezeno, aby se repro chyby atd. Pokud však z nějakého důvodu není možné napodobit produkční prostředí, pak jednotku testování přinejmenším dává yu určitou důvěru ve váš kód, než se dostane do většího testovacího systému.

2
Steve

Uh ... v těchto odpovědích jsou zde uvedeny dobré body ohledně testování jednotek a integrace!

Chybí mi náklady a praktické názory zde. To znamená, že jasně vidím přínos velmi izolovaných/atomových testů jednotek (možná velmi nezávisle na sobě as možností provádět je paralelně a bez závislostí, jako jsou databáze, souborový systém atd.) A testy integrace na vyšší úrovni, ale ... je to také otázka nákladů (čas, peníze, ...) a rizika.

Takže existují i ​​další faktory, které jsou mnohem důležitější (například „co testovat“ ), než si na ně vzpomenete "jak testovat" z mé zkušenosti ...

Platí můj zákazník (implicitně) další částku za psaní a údržbu testů? Je v mém prostředí skutečně nákladově efektivní přístup založený na testech (nejprve napište testy, než napíšete kód) (analýza rizika/nákladů na selhání kódu, lidé, specifikace návrhu, nastavení testovacího prostředí)? Kód je vždy buggy, ale mohlo by být nákladově efektivnější přesunout testování do výrobního využití (v nejhorším případě!)?

Také hodně záleží na tom, jaká je vaše kvalita kódu (standardy) nebo rámce, IDE, principy návrhu atd. Vy a váš tým dodržovat a jak jsou zkušení. Dobře psaný, snadno pochopitelný, dostatečně zdokumentovaný ( ideálně samokumentující), modulární, ... kód zavádí pravděpodobně méně chyb než opak. Skutečné „potřeby“, tlak nebo celkové náklady na údržbu/opravy chyb/rizika pro rozsáhlé testy tedy nemusí být vysoké.

Vezměme to do extrému, kde spolupracovník v mém týmu navrhl, musíme se pokusit jednotce otestovat náš čistý Java model EE modelové vrstvy s požadovaným 100% pokrytím pro všechny třídy uvnitř a zesměšňování databáze Nebo správce, který by chtěl, aby byly integrační testy pokryty 100% všech možných případů použití ve skutečném světě a pracovních postupů na webu, protože nechceme riskovat, že by žádný případ použití selhal. Ale máme omezený rozpočet asi 1 milion EUR, docela nějaký přísný plán na kódování všeho. Prostředí zákazníka, kde by potenciální chyby aplikací nebyly pro člověka nebo společnosti velkým nebezpečím. Naše aplikace bude interně testována (některými) důležitými testy jednotek, testy integrace , klíčové zákaznické testy s navrženými plány zkoušek, zkušební fáze atd. Nevyvíjíme aplikaci pro některou jadernou továrnu nebo farmaceutickou výrobu! (Máme pouze jednu určenou databázi, snadno testovatelný klon pro každý dev a pevně spojený s webappem nebo vrstva modelu)

Já sám se pokusím napsat test, pokud je to možné, a rozvíjet se během testování jednotky kódem. Často to ale dělám z pohledu shora dolů (integrační testování) a snažím se najít ten správný bod, kde je třeba provést „cut vrstvu aplikace“ pro důležité testy (často v modelové vrstvě). (protože se často jedná o „vrstvy“)

Navíc testovací kód jednotky a integrace nepřichází bez negativního dopadu na čas, peníze, údržbu, kódování atd. Je to v pohodě a mělo by být použito, ale s ohledem na důsledky při zahájení nebo po 5 letech mnoha vyvinutých testů kód.

Takže bych řekl, že to opravdu hodně záleží na důvěře a hodnocení nákladů/rizik/přínosů ... jako ve skutečném životě, kde nemůžete a nechcete pobíhat se spoustou nebo 100 % bezpečnostních mechanismů.

Dítě může a mělo by se někde vyšplhat a může spadnout a ublížit se. Auto může přestat fungovat, protože jsem došel do nesprávného paliva (neplatný vstup :)). Toast může být spálen, pokud tlačítko času přestane fungovat po 3 letech. Ale nikdy nechci jet na dálnici a držet svůj oddělený volant v mých rukou :)

0
Andreas Dietrich

Co se týká aspektů designu: Věřím, že i malé projekty těží z testování kódu. Nemusíte nutně představovat něco jako Guice (jednoduchá tovární třída bude často dělat), ale oddělení stavebního procesu od logiky programování má za následek

  • jasně dokumentovat závislosti každé třídy prostřednictvím svého rozhraní (velmi užitečné pro lidi, kteří jsou v týmu noví)
  • třídy se stanou mnohem jasnější a snadnější na údržbu (jakmile dáte vytvoření ošklivého grafu objektů do samostatné třídy)
  • volné spojení (mnohem snazší provádění změn)
0
Oliver Weiler