• 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 som side_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 derefter pytest.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 for with 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 til open 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 i app.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:

  1. vi har ikke brug return_value.
  2. 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 en MagicMock 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 vi made_up_function på de returnerede mock, og på det nyoprettede mock vi set den sidste return_value til 123.,

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 kode

Lad 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å:

  1. spec
  2. 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 en AttributeError.,

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, at mock.MagicMock tilbage vil automagisk skabe de attributter, der er kaldt på mock (dette er fordi open 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.