- Bevezetés
- unittest.,makett, vagy ál
- Lakberendező
- Erőforrás helye
- Mock return_value vs side_effect
- Mock Beágyazott Hívásokat
- Ellenőrizze Kivételek
- Elszámolási lru_cache
- Mock Modul Szint/Globális Változók
- Mock Például Módszer
- Mock Osztály Módszer
- Ál Egész Osztály
- Mock Aszinkron Hívást
- Mock Például Típusok
- Mock beépített
open
függvény - Következtetés
Bevezető
Gúnyos források írásakor vizsgálatok Python zavaró lehet, ha nincs tisztában vele, hogy ilyen dolgokat., Ebben a bejegyzésben fogom fedezni különböző aspektusait gúnyos kód, amely remélhetőleg hasznos forrás azok számára, akik egy kicsit beragadt.
megjegyzés: a kódpéldákban a pytest-et használom, de a legtöbb esetben nem számít.
unittest.mock vagy mock
egy erőforrás “mock” – jához először a mock
modulra lesz szükségünk, és ez az első botlásunk: melyik verzióra van szükségünk? vagyis kettő van, és mindkettő hivatalosnak tűnik (mock
és unittest.mock
).,
a mock
modul egy visszafelé kompatibilis könyvtár letölthető PyPy, ahol a unittest.mock
ugyanaz a dolog, de csak kompatibilis a verzió Python használ.,
Tehát szinte minden esetben azt akarja, hogy importáljuk, így:
import unittest.mock as mock
további lehetőségekért látod, ez a referencia útmutató
Lakberendező
A leggyakoribb módja, hogy kigúnyolja a források használata, a Python lakberendező körül a teszt funkció:
@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123
ebben Az esetben mi vagyunk folt (thing
) lehet egy változó vagy függvény.,
amikor ezt megteszi, át kell adnia egy argumentumot a funkciójához (megnevezheti azt, amit akar†), amely egy MagicMock
lesz.
Ez azt jelenti, hogy ha nem csinálsz mást, akkor a thing
hívás (legalább a fenti példában) a 123
értéket eredményezi.
† a
mock_<noun>
változó megnevezése.,
Ha több dolgot gúnyol, akkor egymásra rakja a modell dekorátorokat, és átadja őket a tesztfunkcióhoz:
forrás helye
fontos tudni, hogy gúnyolódáskor meg kell adnia a gúnyolódítandó erőforrás helyét, amely releváns a ” P ” funkcióhoz.ahol importálják., Ez a legjobb magyarázható például…
Képzeld el, van egy modul app.foo
belül, hogy a modul nem behozatali egy másik függőség, mint így:
from app.bar import thing
Azt gondolhatnánk, hogy amikor telefonál mock.patch
, hogy át egy hivatkozás, hogy az erőforrás, mint a app.bar.thing
. Ez csak akkor lenne releváns, ha az erőforrást a app.foo
modulon belüli teljes elérési úttal hívnák (például ha app.foo
app.bar.thing(...)
).,
Ha a teljes névtér elérési útja nincs hivatkozva, ami nem szerepel a fenti példában (megjegyzés csak a thing
erőforrást importáljuk). Ez azt jelenti, hogy meg kell adnunk a referencia névteret, ahol az importált:
@mock.patch('app.foo.thing')
Tehát annak ellenére, hogy thing
létezik a app.bar
meg kell adnunk app.foo.thing
mint app.foo
itt importáltuk felhasználásra. Ez elkapja az embereket minden alkalommal.,
Mock return_value vs side_effect
Ha a funkció egy try/except körül, akkor használhatjuk a side_effect
okozhat a hívó a funkciót ravaszt Kivételt, mint a visszaadott érték:
@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))
Megjegyzés: ha használt volna,
return_value=Exception('whoops')
akkor a modell visszatér a string ábrázolása inkább Kivétel, mint felnevelni egy kivétel, mint aside_effect
nem.,a módszer egy kigúnyolta tárgy volt a címe, hogy:Az oka, hogy itt még bonyolultabb lesz köszönhető, hogy egy ál visszatér egy új mock elérésekor egy ingatlan egy ál:
A fenti kód error:
AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)
kell, hogy győződjön meg róla, hogy érvényesíthesse a modell a megfelelő pillanatban:
Ellenőrizze Kivételek
Ha szeretnénk ellenőrizni, hogy egy darab kód dob egy
Exception
típus, amikor szükségünk van rá, hogy meg tudjuk ál konkrét források dob egy kivételt, akkor használjuk apytest.raises
mint összefüggésben manager körül a hívó fél a kódot ellenőrizni.,tudjuk fogni, és állításokat ez ellen a várható viselkedés először gúnyos erőforrás szeretnénk dobni egy kivételt, és kap, hogy dobja a saját hamis kivétel a
side_effect
paraméter.ezután adja meg a pontos kivétel típusa számítunk fel a
pytest.raises(T)
:Megjegyzés: ne kövesd el azt a hibát, hogy egyetlen állítások belül a
with
összefüggésben menedzser., Miután a kivételt awith
kontextuskezelőben hívott függvény emeli, a blokk belsejében lévő összes kódot kihagyja.Elszámolási lru_cache
Ha egy funkciót szeretné, hogy tesztelje a
functools.lru_cache
lakberendező alkalmazott, akkor kell figyelnünk a gúnyos válasz, hogy a funkciója, mint ez lesz a gyorsítótárazott egy teszt, valamint a gyorsítótárazott eredmény lesz tért vissza, amikor a függvény hívása újra tesztelni egy másik magatartás (lehet, hogy valószínű, hogy összezavarjon, amikor látod, hogy a váratlan válasz).,a probléma megoldása nagyon egyszerű, mert a
lru_cache
további funkciókat biztosít a funkciók díszítésekor:
cache_info
cache_clear
A ez utóbbi (
cache_clear
) az, amit fel kell hívnia. Ezt az alábbiakban mutatjuk be:megjegyzés: a hibakeresés ez nem mindig nyilvánvaló., Később bemutatom, hogyan kell kigúnyolni a builtin
open
függvényt, és ebben a forgatókönyvben belebotlottam ebbe a problémába, mert bár nem gúnyoltam magát a felső szintű függvényt (gúnyoltam aopen
within), a megnyitott fájl tartalma az volt, amit visszatértek és gyorsítótáraztak.Mock modul szint/globális változók
egy modulváltozóval közvetlenül beállíthatja az értéket, vagy használhatja a
mock.patch
.,a következő példa van a változó
client_id
ami egy globális változó belül aapp.aws
modul importálunk, hogy referencia máshol a kódot:a
mock.patch
példa, van két alapvető dolog, amit észre:
- nem használjuk a
return_value
.- nincs álpéldány át a teszt függvény.,
Ez azért van, mert módosítjuk egy változó, nem pedig egy funkció közvetlen vagy ‘lehívható’, tehát nem kell, hogy átmenjen egy ál a teszt funkció (ha meg szeretné változtatni az érték néhány alkalommal belül magát a vizsgálatot, akkor kigúnyolják a változó, de nem azonnal rendel értéket a dekoratőr).
Mock Instance Method
többféle módon lehet elérni egy példány metódus gúnyolódását., Egy közös megközelítés, hogy a
mock.patch.object
, valahogy így:egy Másik megközelítés, hogy kigúnyolja a módszer, mint egy normális működését, de a referencia módszer segítségével a classname:
egy Másik (bár keménykezű) megközelítés gúnyos egy osztályban például az a módszer, hogy kihasználják azt a tényt, hogy egy Ál visszatér egy új mock például, amikor címe:
@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"
Megjegyzés: a fenti példában gúnyoljuk az egész osztály, amely lehet, hogy nem, hogy mit akarsz. Ha nem, akkor használja az előző
mock.patch.object
példát.,a fenti példa azért működik, mert a
return_value
értéket állítjuk be a modellünkre. Mert ez egyMagicMock
minden attribútum hivatkozott visszatér egy új mock például (egy funkció vagy tulajdonság, egy makett nem létezik), ezért hívjukmade_up_function
a vissza ál, valamint az újonnan létrehozott ál beállítottuk a véglegesreturn_value
, hogy a123
.,de amint azt a fenti megjegyzés is említi, ez a megközelítés kissé túl tompa lehet, attól függően, hogy mi az Ön igényei (függetlenül attól, hogy érdekel-e valami, ami működik, vagy sem).
Mock Class Method
egy osztály metódus kigúnyolásához hasonló megközelítés.,
az egyik megközelítés az lehet, hogy az egész osztályt kigúnyolja (de most van egy kevesebb
return_value
, hogy hozzárendelje):mock_class.ClassMethodName.return_value = "123"
vagy még jobb, ha gúnyolódna, mintha bármilyen normális funkció lenne, de csak hivatkozzon a módszerre az osztályon keresztül:
Mock egész osztály
osztály a
return_value
értéket kell beállítania, hogy az osztály új példánya legyen.,lásd a többi osztályhoz kapcsolódó gúnyos tippeket itt
Mock Async hívások
gúnyos aszinkron kód valószínűleg a leginkább zavaró szempont a gúnyolódás. A “go to” megoldásomat először elmagyarázom, de utána megosztom néhány alternatív módszert, amelyeket a múltban láttam és kipróbáltam.,
Először tekintsük ezt aszinkron kód belsejében egy
app.foo
modul:import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)
Ha meg kell ál a coroutine
app.stuff.some_concurrent_function
, akkor meg tudjuk ezt oldani azáltal, hogy egy funkció úgy működik, mint egy coroutine, valamint lehetővé teszik, hogy beállítható a különböző típusú válaszok:Megjegyzés: a példában tornádó a futó aszinkron teszt.,e alternatívák…
AsyncMock
Megjegyzés: ezt hasznosítja a csomag
pytest-asyncio
, hogy segítsen a vizsgálat asyncio kódkezdjük a kódot, hogy kigúnyolják…
import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y
Most itt van, hogy gúnyolódjon.
Majom Javítás
MagicMock Ns
Aszinkron Inline Függvény
Mock Például Típusok
Ha a gúnyos egy tárgyat fogsz jönni, hogy az ál helyettesíti a teljes objektum, ezért okozhat tesztek át (vagy nem) váratlan módon.,
jelentése, Ha meg kell, hogy egy mock több, mint a Konkrét felület, akkor kétféle módon lehet csinálni, hogy:
spec
wrap
használhatjuk mock
spec
funkció, hogy utánozza az összes módszer / attribútumok az objektum gúnyolódik. Ez biztosítja, hogy a gúnyolódásoknak ugyanaz az api-ja legyen, mint az általuk helyettesített objektumoknak.megjegyzés: van egy szigorúbb
spec_set
, amely felveti aAttributeError
.,ezt legjobban egy példa bizonyítja:
a
wrap
paraméter másrészt lehetővé teszi, hogy “kémkedjen” a megvalósításban, valamint befolyásolja annak viselkedését.,a fenti, hogy kigúnyolja az egész tárgy, nem csak egy módszer:Mock beépített nyitva funkció
Python ál könyvtár rendelkezik egy absztrakció gúnyolódik a beépített
open
funkció sokkal egyszerűbb…A
create=True
param set amock.patch
azt jelenti, hogy amock.MagicMock
vissza fog automatikusan hoz létre attribútumok, hogy hívják a mock (ez azért van, mert aopen
funkció elérésére irányuló próbálkozás sok különböző dolgokat könnyebb makett, hogy gúnyold ki, hogy az ön számára).,következtetés
ott véget érünk. Remélhetőleg ez a lista a gúnyos technikák képes lesz látni végig még a legösszetettebb kódot kell vizsgálni. Hadd tudja, mit gondol a Twitteren.