• Úvod
  • unitest.,vysmívat nebo posmívat
  • Malíř
  • umístění Zdroje
  • Mock return_value vs side_effect
  • Mock Vnořené Volání
  • Ověřte si Výjimky
  • Vymazání lru_cache
  • Mock Úrovni Modulu/Globální Proměnné
  • Mock Metodu Instance
  • Mock Metody Třídy
  • Mock Celé Třídy
  • Mock Asynchronní Volání
  • Ke Stupni Typy
  • Mock builtin open funkce
  • Závěr

Úvod

Zesměšňovat zdrojů při psaní testů v jazyce Python může být matoucí, pokud jste obeznámeni s dělat takové věci., V tomto příspěvku se budu pokrývají různé aspekty zesměšňovat kód, který snad bude užitečný zdroj pro ty, kteří jsou trochu zasekl.

Poznámka: v příkladech kódu používám pytest, ale většinou na tom nezáleží.

unitest.mock nebo mock

abychom mohli „zesměšnit“ zdroj, budeme nejprve potřebovat modul mock a toto je náš první kámen úrazu: kterou verzi potřebujeme? tj. existují dva a oba vypadají jako oficiální (mock a unittest.mock).,

mock modul je zpětně kompatibilní knihovny si můžete stáhnout z PyPy, kde jako unittest.mock je to totéž, ale kompatibilní pouze s verzi Pythonu používáte.,

téměř ve všech případech budete chtít importovat, tak jako:

import unittest.mock as mock

další příklady, viz tato referenční příručka

Malíř

nejčastějším způsobem zesměšňovat zdrojů je použít Python malíř kolem své testovací funkce:

@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123

V tomto případě, co jsme záplatování (thing), může být proměnná nebo funkce.,

když to uděláte, budete muset předat argument vaší funkci (můžete ji pojmenovat, co chcete†), což bude MagicMock.

To znamená, že pokud nechcete dělat nic jiného, pak volání do thing (v příkladu výše alespoň) výsledek hodnoty 123 je vrácena.

† konvence má pojmenovat proměnnou mock_<noun>.,

Pokud jste zesměšňovat více věcí, pak budete stack mock dekoratérů tý navzájem, a předat je spolu s cílem testu funkce:

@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...

umístění Zdroje

je důležité vědět, že když se vám vysmívá by měla určit umístění zdroje k být zesměšňován, relevantní tam, kde je to dovezené., Toto je nejlépe vysvětlit pomocí příkladu…

Představte si, že mám modul app.foo a v rámci tohoto modulu importovat další závislost jako tak:

from app.bar import thing

možná Si myslíte, že když budete volat mock.patch že budete předat odkaz na zdroj, jako je app.bar.thing. Že by relevantní, pouze pokud zdroj byl nazýván s plnou cestou do app.foo modul (např. pokud app.foo app.bar.thing(...)).,

Pokud je plný názvů cesta není odkazováno, což není ve výše uvedeném příkladu (všimněte si dovážíme jen thing zdroj). To znamená, že musíme určit referenční názvů zesměšňovat jako, kde je dovážené:

@mock.patch('app.foo.thing')

i když thing existuje v app.bar nastavit app.foo.thing app.foo je místo, kde jsme dovážené pro použití. To chytí lidi po celou dobu.,

Mock return_value vs side_effect

Pokud vaše funkce má try/s výjimkou okolí, pak můžete použít side_effect způsobí volání funkce vyvolat Výjimku, protože vrácená hodnota:

@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))

Poznámka: pokud byste měli použít return_value=Exception('whoops') mock vrátí řetězec reprezentace spíše Výjimkou než vyvolá výjimku, jako je side_effect.,metoda na posmívali objekt byl nazýván:

důvod, proč to může být složitější, je vzhledem k tomu, jak falešný vrátí nový zesměšňovat, kdy přístup k nemovitosti na makety:

výše uvedený kód bude chyba:

AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)

Budete muset ujistěte se, že budete tvrdit, zesměšňovat ve správný čas:

Ověřit Výjimky

chceme-Li ověřit, že nějaký kus kódu vyvolá Exception typu, když potřebujeme, aby to můžeme zesměšňovat určité zdroje na vyhodí výjimku, a pak použít pytest.raises jako kontext manager kolem volajícího našeho kódu, které mají být ověřeny.,

můžeme chytit a udělat tvrzení proti tomuto očekávanému chování tím, že první zesměšňovat zdrojů chceme hodit výjimku a dostat to hodit naše vlastní falešné výjimky pomocí side_effect parametr.

dále jsme určit přesný typ výjimky jsme čekali, že být zvýšen pomocí pytest.raises(T):

Poznámka: nedělejte tu chybu, že uvedení nějaké tvrzení v with context manager., Jednou Výjimkou je aktivována funkce volána uvnitř with context manager, celý kód poté, co je uvnitř bloku, je přeskočen.

Zúčtování lru_cache

je-Li funkce, kterou chcete testovat má functools.lru_cache malíř nanáší, pak musíte dbát na posměšné reakce, že funkce, jak to bude mezipaměti v jednom testu a mezipaměti výsledek bude vrácena při volání funkce znovu vyzkoušet některé jiné chování (a možná pravděpodobné, že zmást, když vidíte neočekávané reakce).,

K vyřešení tohoto problému je velmi snadné, protože lru_cache poskytuje další funkce, když decoratoring své funkce, poskytuje:

  • cache_info
  • cache_clear

druhý (cache_clear), je to, co budete potřebovat, aby se volání. To je ukázáno níže:

Poznámka: ladění to není vždy zřejmé., Později jsem se demonstrovat, jak se posmívat builtin open funkce, a v tom případě jsem narazil na tento problém, protože i když jsem nebyl výsměšný nejvyšší úrovni funkce sama o sobě (jsem byl výsměch volání open do), obsah souboru, který je otevřen byl, co byl se vrátil a mezipaměti.

Mock Úrovni Modulu/Globální Proměnné

S modulem variabilní, můžete buď nastavit hodnotu přímo, nebo pomocí mock.patch.,

V následujícím příkladu máme proměnnou client_id, která je globální proměnnou uvnitř app.aws modul, který dovážíme na odkaz jinde v našem kódu:

V mock.patch například, tam jsou dvě klíčové věci, aby se upozornění:

  1. nepoužívejte return_value.
  2. neexistuje žádná falešná instance předaná testovací funkci.,

je To proto, že upravujeme proměnné a není přímou funkcí nebo ‚callable‘, takže není třeba předat mock do testovací funkce (pokud chcete změnit hodnotu pár krát během samotné zkoušky pak by mock proměnné, ale ne okamžitě přiřadit hodnotu v dekoratér).

metoda Mock Instance

existuje několik způsobů, jak dosáhnout zesměšňování metody instance., Jeden společný přístup je použití mock.patch.object, tak jako:

Další možností je zesměšňovat metoda, jako by to byla normální funkce, ale ty referenční metoda přes classname:

Další (i když více heavy handed) přístup pro zesměšňovat třída, instance, metoda, je využít fakt, že Vysmívat se vrátí nový mock instance, když volal:

@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"

Poznámka: ve výše uvedeném příkladu jsme zesměšňovat celou třídu, což nemusí být to, co chcete. Pokud tomu tak není, použijte místo toho předchozí příklad mock.patch.object.,

důvod, proč výše uvedený příklad funguje, je proto, že jsme nastavení return_value na našem mock. Protože se jedná o MagicMock každý atribut odkazuje vrací nový mock stupně (funkce nebo vlastnost, kterou zavolat na falešný nemusí existovat), a tak říkáme made_up_function vrátil makety, a na to, že nově vytvořené makety jsme si stanovili konečné return_value 123.,

ale jak je uvedeno ve výše uvedené poznámce, tento přístup může být příliš tupý v závislosti na tom, jaké jsou vaše potřeby (ať už vám záleží na tom, zda máte nějakou funkční třídu nebo ne).

metoda falešné třídy

zesměšňovat metodu třídy je podobný přístup k zesměšňování metody instance.,

Jeden přístup by mohl být, že se vysmíváte celou třídu (ale nyní máte jeden méně return_value přiřadit k):

mock_class.ClassMethodName.return_value = "123"

Nebo ještě lépe, měli byste zesměšňovat to, jako byste jakékoliv normální funkce, ale jen referenční metoda pomocí třídy:

Mock Celé Třídy

zesměšňovat celou třídu, musíte nastavit return_value nové instance třídy.,

Viz další třídy související zesměšňovat tipy tady,

Mock Asynchronní Volání

Zesměšňovat asynchronní kód je pravděpodobně nejvíce matoucí aspekt zesměšňovat. Moje řešení „go to“ nejprve vysvětlím, ale poté budu sdílet některé alternativní metody, které jsem viděl a vyzkoušel v minulosti.,

Nejprve zvážit tento asynchronní kód uvnitř app.foo modul:

import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)

Pokud potřebujeme zesměšňovat coroutine app.stuff.some_concurrent_function, pak můžeme vyřešit tím, že vytvoří funkci, která působí jako coroutine a nechte ji být konfigurovatelné pro různé typy reakcí:

Poznámka: tento příklad používá tornado pro běh asynchronní test.,e alternativ…

AsyncMock

Poznámka: to využívá balíček pytest-asyncio pomoci s testováním asyncio kód,

Pojďme začít s kódem, aby se vysmíval…

import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y

Nyní zde je návod, jak bychom se vysmívat…

Monkey Patch

MagicMock Podtřídy

Asynchronní Inline Funkce

Mock Instance Typů

Když se vysmívá objekt, zjistíte, že vysmívat se nahrazuje celý objekt, a tak to může způsobit, že testy předávat (nebo selhání) v neočekávaných cestách.,

to Znamená, pokud potřebujete, aby se falešný více jako konkrétní rozhraní, pak existují dva způsoby, jak to udělat:

  1. spec
  2. wrap

můžeme použít mock spec funkce napodobovat všechny metody/atributy objektu, který je terčem posměchu. Tím je zajištěno, že vaše posměšky mají stejné api jako objekty, které nahrazují.

Poznámka: je přísnější spec_set, který zvýší AttributeError.,

Toto je nejlépe vidět na příkladu:

wrap parametr na druhou stranu umožňuje „špionážní“ na provádění, jakož i vliv na jeho chování.,na výše uvedené, které se vysmívá celý objekt, ne pouze jednu metodu:

Mock builtin otevřít funkce

Python mock knihovna poskytuje abstrakci pro zesměšňovat builtin open funkce mnohem jednodušší.

create=True param nastaveny na mock.patch znamená, že mock.MagicMock vrátil se automagicky vytvořit libovolné atributy, které jsou tzv. na falešný (je to proto, že open funkce se pokusí o přístup k spoustu různých věcí, a je to jednodušší pro posmívat, vysmívat se všem, že pro vás).,

závěr

tam skončíme. Doufejme, že tento seznam posměšných technik vás bude moci vidět i přes nejsložitější kód, který je třeba otestovat. Dejte mi vědět, co si myslíte na Twitteru.