- introduktion
- unittest.,håne eller spotte
- Dekoratør
- Ressource placering
- Mock return_value vs side_effect
- Mock Indlejrede Opkald
- Bekræft Undtagelser
- Clearing lru_cache
- Mock-Modul Niveau/Globale Variabler
- Mock Eksempel på Metode
- Mock Klasse-Metode
- Mock Hele Klassen
- Mock Async Opkald
- Mock Instans Typer
- Mock indbyggede
open
funktion - Konklusion
Indledning
Spottende ressourcer, når du skriver tests i Python kan være forvirrende, hvis du ikke er fortrolig med at gøre sådanne ting., I dette indlæg vil jeg dække forskellige aspekter af spotkode, som forhåbentlig vil være en nyttig ressource for dem, der sidder lidt fast.
Bemærk: i kodeeksemplerne bruger jeg pytest, men for det meste burde det ikke være noget.
unittest.mock eller mock
for at ‘mocke’ en ressource har vi først brug for mock
– modulet, og dette er vores første snubler: hvilken version har vi brug for? dvs. der er to, og de ser begge ud til at være officielle (mock
og unittest.mock
).,
mock
modul er en baglæns kompatibelt bibliotek, som du kan downloade fra PyPy, hvor som unittest.mock
de samme ting, men kun kompatibel med den version af Python, du bruger.,
Så i næsten alle tilfælde vil du ønsker at importere det som så:
import unittest.mock as mock
For flere eksempler se denne reference guide
Dekoratør
Den mest almindelige måde at spotte ressourcer er at bruge en Python dekoratør omkring din test funktion:
@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123
I dette tilfælde, hvad vi patching (thing
) kan være en variabel eller en funktion.,
Når du gør dette, skal du overføre et argument til din funktion (Du kan navngive det, hvad du vil†), som vil være et MagicMock
.
dette betyder, at hvis du ikke gør noget andet, vil opkald til thing
(i eksemplet ovenfor mindst) resultere i, at værdien 123
returneres.
convention konvention er at navngive variablen
mock_<noun>
.,
Hvis du er spottende flere ting, så vil du stak mock dekoratører ontop af hinanden, og videregive dem sammen for at test funktion:
@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...
Ressource placering
Det er vigtigt at vide, at når spottende, skal du angive placeringen af den ressource, for at blive hånet, relevante til, hvor det er importeret., Det er bedst forklares med et eksempel…
Forestil dig, at jeg har et modul app.foo
og inden, at modul i import anden afhængighed som så:
from app.bar import thing
tror Du måske, at når du kalder mock.patch
som du passerer det en henvisning til den ressource som app.bar.thing
. Det ville kun være relevant, hvis ressourcen blev kaldt med den fulde sti inden for app.foo
– modulet (f.eks. hvis app.foo
kaldet app.bar.thing(...)
).,
Hvis der ikke henvises til den fulde navneområdesti, som den ikke er i ovenstående eksempel (bemærk, at vi kun importerer thing
ressource). Det betyder, at vi er nødt til at angive reference namespace til at spotte, hvor det er importeret:
@mock.patch('app.foo.thing')
Så selvom thing
eksisterer inden i app.bar
vi angiver app.foo.thing
som app.foo
er der, hvor vi har importeret det til brug. Dette fanger folk ud hele tiden.,
Mock return_value vs side_effect
Hvis din funktion er en prøve/bortset fra omkring det, så kan du bruge side_effect
for at forårsage den kaldende funktion til at udløse en Undtagelse, da den returnerede værdi er:
@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))
Bemærk: hvis du havde brugt
return_value=Exception('whoops')
så mock ville returnere string repræsentation af Undtagelsen snarere end at hæve en undtagelse somside_effect
gør.,metode på en hånede objekt blev kaldet:grunden til at dette kan blive mere kompliceret, er på grund af, hvordan en mock vil vende tilbage med en ny mock, når adgang til en ejendom på en mock:
ovenstående kode vil fejl:
AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)
Du skal sikre dig du gøre den mock på det rigtige tidspunkt:
Bekræft Undtagelser
Hvis vi ønsker at kontrollere, at nogle stykke kode, der kaster en
Exception
type, når vi har brug for det for at vi kan håne specifikke ressourcer til at kaste en undtagelse, og brug derefterpytest.raises
som en sammenhæng manager omkring opkalds af vores kode til at blive kontrolleret.,Vi kan fange og fremsætte påstande mod denne forventede opførsel ved først at spotte den ressource, vi vil kaste en undtagelse, og få den til at kaste vores egen falske undtagelse ved hjælp af parameteren
side_effect
.Næste vi angive den nøjagtige undtagelse type vi forventer at være rejst ved hjælp af
pytest.raises(T)
:Bemærk: du må ikke begå den fejl at sætte alle påstande i
with
forbindelse manager., Når undtagelsen er hævet af den funktion, der kaldes inden forwith
Conte .t manager, springes al kode efter den inde i blokken over.Clearing lru_cache
Hvis en funktion, du ønsker at test har den
functools.lru_cache
dekoratør, der anvendes, så du bliver nødt til at være opmærksomme på spottende svar, der fungerer som det vil være cachet i en test og den cachelagrede resultatet vil blive returneret, når kalde funktionen igen for at teste en anden adfærd (og måske sandsynligvis forvirre dig, når du ser det uventede reaktion).,for At løse dette problem er meget let, fordi
lru_cache
giver yderligere funktioner, når decoratoring funktioner, det giver:
cache_info
cache_clear
Den sidstnævnte (
cache_clear
), hvad ville du nødt til at kalde. Dette er vist nedenfor:Bemærk: Fejlfinding Dette er ikke altid indlysende., Senere demonstrerer jeg, hvordan man håner den indbyggede
open
– funktion, og i det scenarie snublede jeg over dette problem, for selv om jeg ikke spottede selve funktionen på øverste niveau (jeg spottede opkaldet tilopen
inden for), var indholdet af filen, der blev åbnet, det, der blev returneret og blev cachelagret.Mock-Modul Niveau/Globale Variabler
Med en modul-variablen kan du enten kan angive værdien direkte eller bruge
mock.patch
.,I det følgende eksempel har vi den variabel
client_id
, som er en global variabel, inde iapp.aws
modul, som vi importerer til at henvise til andre steder i vores kode:I
mock.patch
eksempel er der to vigtige ting at bemærke:
- vi har ikke brug
return_value
.- Der er ingen mock instans videre til testfunktionen.,
Dette skyldes, at vi ændrer en variabel og ikke en direkte funktion eller ‘callable’, så der er ingen grund til at videregive en mock til testfunktionen (hvis du vil ændre værdien et par gange inden for selve testen, ville du mocke variablen, men ikke straks tildele en værdi i dekoratøren).
Mock Instance Method
Der er flere måder at opnå mocking af en instansmetode., En fælles strategi er at bruge
mock.patch.object
, som så:en Anden tilgang er at håne den metode, som du ville med en normal funktion, men du reference metode via klassenavn:
en Anden (selv om mere hårdhændet) tilgang til spottende et klasse eksempel på metode er at drage fordel af det faktum, at en Mock vil vende tilbage med en ny mock eksempel når der bliver kaldt:
@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"
Bemærk: i ovenstående eksempel har vi håne hele klassen, og som måske ikke være, hvad du ønsker. Hvis ikke, skal du bruge det forrige
mock.patch.object
eksempel i stedet.,årsagen til, at ovenstående eksempel fungerer, er fordi vi indstiller
return_value
på vores mock. Fordi dette er enMagicMock
hver attribut, der refereres returnerer en ny mock instans (en funktion eller en ejendom, du kalder på en mock behøver ikke at eksistere) og så kalder vimade_up_function
på de returnerede mock, og på det nyoprettede mock vi set den sidstereturn_value
til123
.,men som nævnt i ovenstående note kan denne tilgang være lidt for stump afhængigt af hvad dine behov er (uanset om du er interesseret i, om du har en funktionsklasse eller ej).
Mock Class Method
at håne en klassemetode er en lignende tilgang til at håne en instansmetode.,
En tilgang kunne være, at du håner hele klassen (men nu har du en mindre
return_value
for at tildele):mock_class.ClassMethodName.return_value = "123"
Eller endnu bedre, bør du spotte det, som du ville enhver normal funktion, men bare henvise til den metode, der via klassen:
Mock Hele Klassen
for At spotte en hel klasse skal du indstille
return_value
for at være en ny instans af klassen.,Se anden klasse relaterede spottende tips her
Mock Async Opkald
Spottende asynkron kode er sandsynligvis den mest forvirrende aspekt af spottende. Min ‘gå til’ løsning jeg vil forklare først, men efter at jeg vil dele nogle alternative metoder, jeg har set og prøvet i fortiden.,
Først overveje denne asynkron kode inde i en
app.foo
modulet:import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)
Hvis vi er nødt til at håne coroutine
app.stuff.some_concurrent_function
, så vi kan løse dette ved at oprette en funktion, der fungerer som en coroutine, og lad det være konfigureres til forskellige typer af svar:Bemærk: eksempel bruger tornado for at køre en asynkron test.,e alternativer…
AsyncMock
Bemærk: dette udnytter pakke
pytest-asyncio
for at hjælpe med at teste asyncio kodeLad os starte med koden for at blive hånet…
import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y
Nu her er, hvordan vi havde mock det…
Monkey Patch
MagicMock Underklasse
Async Inline-Funktion
Mock Instans Typer
Når spottende et objekt, du vil finde, at håne erstatter hele objektet, og det kan forårsage prøver at passere (eller ikke har) på uventede måder.,
hvilket Betyder, hvis du har brug for til at lave en mock mere som det konkrete interface, så der er to måder at gøre det på:
spec
wrap
Vi kan bruge mock
spec
funktion til at efterligne alle metoder/attributterne for objektet bliver hånet. Dette sikrer, at dine spotter har den samme api som de objekter, de erstatter.Bemærk: der er en strengere
spec_set
, som vil hejse enAttributeError
.,dette demonstreres bedst med et eksempel:
wrap
parameteren på den anden side giver dig mulighed for at ‘spionere’ på implementeringen såvel som påvirke dens opførsel.,af det ovenstående, at håner det hele, ikke bare en enkelt metode:Mock indbyggede åben funktion
Python ‘ s mock-bibliotek, som tilbyder en abstraktion for at håne den indbyggede
open
funktion en masse enklere…
create=True
param sæt påmock.patch
betyder, atmock.MagicMock
tilbage vil automagisk skabe de attributter, der er kaldt på mock (dette er fordiopen
funktion vil forsøge at få adgang til masser af forskellige ting, og det er lettere for mock at spotte ud af alt dette for dig).,konklusion
der slutter vi. Forhåbentlig vil denne liste over mocking teknikker kunne se dig igennem selv den mest komplekse kode, der skal testes. Lad mig vide hvad du synes på T .itter.