- Ú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 jeside_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žítpytest.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í:
- nepoužívejte
return_value
.- 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á oMagicMock
každý atribut odkazuje vrací nový mock stupně (funkce nebo vlastnost, kterou zavolat na falešný nemusí existovat), a tak říkámemade_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:
spec
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 namock.patch
znamená, žemock.MagicMock
vrátil se automagicky vytvořit libovolné atributy, které jsou tzv. na falešný (je to proto, žeopen
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.