je to jen fakt života, protože kód roste nakonec budete muset začít přidávat posměšky do testovací sady. To, co začalo jako roztomilý malý projekt dvou tříd, nyní mluví s externími službami a už ho nemůžete pohodlně otestovat.

to je důvod, proč Python lodě s unittest.mock, výkonný součástí standardní knihovny pro klučky závislostí a zesměšňovat vedlejší účinky.

unittest.mock však není příliš intuitivní.,

našla jsem mnohokrát přemýšlel, proč můj recept nefunguje pro konkrétní případ, tak jsem dal dohromady tento taháku pomoci sobě a ostatním se vysmívá pracovat rychle.

příklady kódu najdete v úložišti GitHub v článku. Budu používat Python 3.6, pokud používáte 3.2 nebo níže, budete muset použít balíček mock PyPI.,

příklady jsou psány pomocí unittest.TestCase kurzy pro jednoduchost v provedení bez závislostí, ale můžete je zapsat jako funkce pomocí pytest téměř přímo,unittest.mock bude fungovat v pohodě. Pokud jste uživatel pytest, doporučuji vám podívat se na vynikající knihovnu pytest-mock.

středový bod modulu unittest.mock je samozřejmě třída Mock., Hlavní charakteristikou Mock objektu je, že se vrátí další Mock například, když:

  • přístup k jedné z jeho atributy
  • volání samotný objekt

Toto je výchozí chování, ale to může být přepsána v různými způsoby. Například můžete přiřadit hodnotu atributu v Mock od:

  • Přiřadit přímo, jako byste dělat s žádnou Python objektu.,
  • použijte metodu configure_mock na instanci.
  • nebo předejte argumenty klíčových slov do třídy Mock při vytváření.

přepsat volání do makety musíte konfigurovat jeho return_value majetek, k dispozici také jako klíčové slovo argument v Mock inicializátor., Mock bude vždy vrátit stejnou hodnotu pro všechny hovory, znovu, to může také být nakonfigurován tak pomocí side_effect atribut:

  • pokud chcete vrátit jiné hodnoty na každé volání můžete přiřadit iterable side_effect.
  • Pokud chcete při volání makety vyvolat výjimku, můžete objekt výjimky jednoduše přiřadit side_effect.,

všechny tyto nástroje nyní můžeme vytvořit pahýly pro v podstatě jakýkoli Python objekt, který bude pracovat skvělé pro vstupy do našeho systému. Ale co výstupy?

Pokud vytváříte program CLI ke stažení celého Internetu, pravděpodobně nechcete stahovat celý Internet na každém testu. Místo toho by stačilo tvrdit, žerequests.download_internet (nikoli skutečná metoda) byla volána vhodně. Mock vám dává užitečné metody.,

Poznámka: v našem příkladu assert_called_once nepodařilo, to vitríny další klíčový aspekt Mock objekty, zaznamenávat všechny interakce s nimi, a pak můžete prohlédnout tyto interakce.

například, můžete použít call_count načíst počet volání na Mock a pomocí call_args nebo call_args_list zkontrolujte, zda nejsou argumenty do posledního nebo všech hovorů v tomto pořadí.,

Pokud je to nevhodné v každém okamžiku můžete použít reset_mock metoda k vymazání zaznamenaných interakcí, poznámka: konfigurace nebude resetovat, jen interakce.

a Konečně, dovolte mi představit MagicMock, podtřídu Mock, který implementuje výchozí magie nebo dunder metody. Díky tomu je MagicMock ideální pro zesměšňování chování třídy, a proto je to výchozí třída při záplatování.,

nyní jsme připraveni začít zesměšňovat a izolovat jednotku pod testy. Zde je několik receptů, aby mějte na paměti:

Náplasti na dovozní

hlavní způsob, jak používat unittest.mock je patch dovoz v modulu v rámci testu pomocí patch funkce.

patch budou zachytit import prohlášení identifikovány řetězec (více o tom později), a vrátit Mock například můžete nakonfigurovat pomocí technik, které jsme diskutovali výše.,

Představte si, že chceme vyzkoušet tento velmi jednoduchý funkce:

Všimněte si, že jsme import os volat getcwd získat aktuální pracovní adresář. Nechceme to skutečně nazvat na našich testech, protože to není důležité pro náš kód a návratová hodnota se může lišit mezi prostředími, na kterých běží.

jak je uvedeno výše, musíme dodatpatch řetězcem představujícím náš konkrétní import., Nechceme, aby nabídky jednoduše os.getcwd vzhledem k tomu, že by patch pro všechny moduly, namísto toho se chceme dodat modul v rámci testu je import os , např. work.os . Když je modul importován patch bude pracovat své kouzlo a vrátit Mock místo.,

Alternativně, můžeme použít dekoratér verze patch poznámka: tento test má navíc parametr: mocked_os, který Mock je vstřikován do testu.,

patch předá klíčové slovo argumenty Mock třídy, takže konfigurace return_value jednoduše přidat jako jednu:

Zesměšňovat třídy

je To docela běžné opravy tříd zcela nebo částečně., Helper "db"

  • Worker vrací očekávané cestě poskytnuté Helper
  • Za účelem testování Worker v úplné izolaci potřebujeme na opravu celé Helper třídy:

    Poznámka: double return_value v příkladu, jednoduše pomocí MockHelper.get_path.return_value nebude fungovat, protože v kódu: get_path na instanci, ne samotné třídy.,

    řetězení syntaxe je mírně matoucí, ale pamatujte si, MagicMock vrací další MagicMock hovory __init__. Tady jsme konfiguraci žádné falešné Helper instance vytvořené MockHelper vrátit to, co očekáváme, že na volání do get_path což je jediný způsob, staráme se o tom v našem testu.,

    Class speccing

    o důsledek flexibility, Mock je, že jakmile jsme se posmívali třídy Python nebude zvyšovat AttributeError tak to prostě vrátí nové instance MagicMock pro v podstatě všechno. To je obvykle dobrá věc, ale může vést k nějaké matoucí chování a potenciálně chyby. Například psaní následující test,

    bude tiše projít bez varování zcela chybí překlep v assrt_called_once .,

    Navíc, pokud bychom měli přejmenovat Helper.get_path Helper.get_folder, ale nezapomeňte aktualizovat volání v Worker naše testy budou ještě projít:

    a Naštěstí Mock přichází s nástrojem, aby se zabránilo tyto chyby, speccing.

    jednoduše řečeno, předkonfiguruje zesměšňování, aby reagovalo pouze na metody, které skutečně existují ve třídě spec., Existuje několik způsobů, jak definovat specifikace, ale nejjednodušší je jednoduše složit autospec=True patch hovor, který se bude konfigurovat Mock chovat jako objektu, který je terčem posměchu, získávání výjimky pro chybějící atributy a nesprávné podpisy podle potřeby., Například:

    Částečné třídy zesměšňovat

    Pokud jste méně náchylní k testování v naprosté izolaci můžete také částečně patch třídy pomocí patch.object:

    Zde patch.object což nám umožňuje konfigurovat posmívali verze get_path, přičemž zbytek chování beze změny., Samozřejmě to znamená, že test již není to, co byste striktně zvažovali test jednotky, ale s tím můžete být v pořádku.

    Zesměšňovat vestavěné funkce a proměnné prostředí

    V předchozích příkladech jsme zanedbali test jeden konkrétní aspekt naší jednoduché třídy, print volání. Pokud je to v kontextu vaší aplikace důležité, například příkaz CLI, musíme proti tomuto volání vznést tvrzení.,

    print je, samozřejmě zabudovanou funkci v Pythonu, což znamená, že není třeba importovat na náš modul, který jde proti tomu, co bylo uvedeno výše o záplatování na import., Ještě představte si, měli jsme to trochu složitější verze naší funkce

    můžeme napsat test, jako je následující:

    Všimněte si pár věcí:

    1. můžeme vysmívat print s tím žádný problém a tvrdí, tam byl hovor, v návaznosti na „patch na import“ pravidlo. Jednalo se však o změnu zavedenou ve 3.,5, dříve jste potřebovali přidat create=True patch volání pro signál unittest.mockvytvořit Mock i když žádné dovozní odpovídá identifikátor.
    2. jsme pomocí patch.dict aplikovat dočasné proměnné prostředí v os.environ, to je rozšiřitelný do jiných slovník bychom chtěli zesměšňovat.,
    3. jsme hnízdění několika patch context manager hovorů, ale pouze pomocí as v první z nich, protože to je jediná musíme použít k volání assert_called_once_with .,

    Pokud si nejste rádi hnízdí kontextu manažeři také můžete napsat patch hovory v dekoratér formy:

    Všimněte si však pořadí argumentů k testu odpovídá pořadí překrývání malíři, a také to, že patch.dict nemá aplikovat argument.,tento velmi běžný scénář:

    můžete, samozřejmě, přidat skutečné svítidlo souboru, ale v reálném světě případech nemusí být možnost, místo toho můžeme vysmívat souvislosti manager je výstup StringIO předmět:

    není nic zvláštního, kromě magie __enter__ metoda, jen musíme mít na paměti základní mechaniky z kontextu manažery a některé chytré využití našeho věrného MagicMock .,

    zesměšňující atributy třídy

    existuje mnoho způsobů, jak toho dosáhnout, ale některé jsou více bláznivé, že ostatní. Že jste napsal následující kód:

    můžete otestovat kód bez jakékoliv vysmívá dvěma způsoby:

    1. Pokud kód v rámci testu přistupuje k atributu pomocí self.ATTRIBUTE, což je případ v tomto příkladu, můžete jednoduše nastavit atribut přímo v instanci. To je poměrně bezpečné, protože změna je omezena na tuto jedinou instanci.,
    2. alternativně můžete také nastavit atribut v importované třídě v testu před vytvořením instance. To se však změní atribut class jste dovezené v testu, které by mohly mít vliv na následující testy, takže si musíte pamatovat, aby jej resetovat.

    hlavní nevýhodou z tohoto non-Mock přístupů je, že pokud budete kdykoliv přejmenovat atribut vaše testy selžou a chyba nebude přímo poukázat toto pojmenování nesoulad.,

    k vyřešení, že můžeme použít patch.object na importované třídě, která si bude stěžovat, pokud třída nemá zadaný atribut.

    Zde jsou některé testy, pomocí každé techniky:

    Zesměšňovat třídy pomocníci

    následující příklad je kořen mnoha problémů, pokud jde monkeypatching pomocí Mock. Obvykle se objevuje na vyspělejších kódových základech, které začínají využívat rámce a pomocníky při definici třídy., Například, představte si tuto hypotetickou Field class helper:

    Jeho účelem je zabalit a zvýšit atribut v jiné třídě, poměrně vzor jste běžně mohli vidět v ORMs nebo formuláře knihovny. Nedělejte si příliš starosti s jeho podrobnostmi, stačí si uvědomit, že existuje type a default hodnota předána.,

    Teď vzít další vzorek výrobní kód:

    Tato třída využívá Field třídy určením jeho country atribut jako ten, jehož typ je str default jako první prvek COUNTRIES konstantní. Testovaná logika spočívá v tom, že v závislosti na zemi se uplatňuje sleva.,

    Pro který můžeme napsat následující test:

    Ale to by NEMĚLO projít.,

    Pojďme se projít test:

    1. První to skvrny výchozí zemí v pricer seznam s jedním vstupem GB ,
    2. To by mělo CountryPricer.country atribut default na ten vstup, protože to je definice zahrnuje default=COUNTRIES
    3. To pak instanciates CountryPricer a žádá za zvýhodněnou cenu pro GB !

    tak co se děje?,

    příčinou tohoto spočívá v Pythonu chování při importu, nejlepší je popsáno v Luciano Ramalho je vynikající Plynulý Python na kapitole 21:

    Pro třídy, příběh je jiný: při dovozu čas, tlumočník vykonává tělo každé třídě, i tělo třídy vnořené v jiných třídách. Provedení těla třídy znamená, že jsou definovány atributy a metody třídy a pak je vytvořen objekt třídy.,

    Použití toto na našem příkladu:

    1. country atribut Field instance je postaven před zkouškou se běžel na čas importu,
    2. Python přečte tělo třídy, absolvování COUNTRIES, který je definován v tomto bodě Field instance
    3. Naše testovací kód běží, ale je příliš pozdě pro nás chcete-patch COUNTRIES a získat správné tvrzení.,

    Z výše uvedeného popisu, můžete zkusit zpoždění import modulu, dokud uvnitř zkoušky, něco jako:

    Toto, nicméně, bude to stále ještě není předat jako mock.patch importovat modul a pak monkeypatch, což má za následek stejné situaci jako předtím.,

    Chcete-li obejít toto musíme přijmout stav CountryPricer třídy v době testu a patch default na již inicializován Field příklad:

    Toto řešení není ideální, protože to vyžaduje znalosti z niternosti Field, kterou vy nemáte, nebo chcete použít, pokud používáte externí knihovnu, ale funguje to na tento poněkud zjednodušený případ.,

    tento problém s časem importu je jedním z hlavních problémů, se kterými jsem se setkal při použití unittest.mock. Při používání je třeba si uvědomit, že při importu je spuštěn kód na nejvyšší úrovni modulů, včetně třídních orgánů.

    Pokud je logika, kterou testujete, závislá na kterékoli z této logiky, možná budete muset přehodnotit, jak používáte patch.,

    Balení

    Tento úvod a cheatsheet by se vysmíváš se s unittest.mock, ale doporučuji si přečíst dokumentaci důkladně, je tam spousta další zajímavé triky naučit.

    je To stojí za zmínku, že tam jsou alternativy k programu unittest.mock, zejména Alex Gaynor je pretend knihovny v kombinaci s pytestmonkeypatch svítidlo. Jak Alex zdůrazňuje, pomocí těchto dvou knihoven můžete zaujmout jiný přístup, aby se zesměšňování stalo ještě více předvídatelným., Rozhodně přístup stojí za prozkoumání, ale mimo rozsah tohoto článku.

    unittest.mock je v současné době standard pro zesměšňovat v Pythonu a najdete ho prakticky v každé codebase. Mít solidní pochopení toho vám pomůže psát lepší testy rychleji.

    Articles

    Napsat komentář

    Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *