- Inleiding
- unittest.,mock of mock
- Decorateur
- Informatiebron locatie
- Mock return_value vs side_effect
- Mock Geneste Aanroepen
- Controleer de Uitzonderingen
- het verwijderen van lru_cache
- Mock-Module Niveau/Globale Variabelen
- Mock Exemplaar Methode
- Mock-Methode van de Klasse
- Mock Hele Klas
- Mock Asynchrone Oproepen
- Mock Aanleg Types
- Mock ingebouwde
open
functie - Conclusie
Inleiding
het Bespotten van bronnen bij het schrijven van tests in Python kan verwarrend zijn als je niet bekend bent met het doen van zulke dingen., In dit bericht ga ik verschillende aspecten van spot code te dekken, die hopelijk een nuttige bron voor degenen die een beetje vast zitten.
opmerking: in de code voorbeelden gebruik ik pytest, maar voor het grootste deel zou dat er niet toe moeten doen.
unittest.mock or mock
om een bron te’ mock ‘ hebben we eerst de mock
module nodig, en dit is ons eerste struikelblok: welke versie hebben we nodig? er zijn er twee en beide lijken officieel (mock
en unittest.mock
).,
demock
module is een backwards compatible bibliotheek die u kunt downloaden van PyPy, waar alsunittest.mock
hetzelfde is, maar alleen compatibel met de versie van Python die u gebruikt.,
So in bijna alle gevallen zult u het als volgt willen importeren:
import unittest.mock as mock
voor meer voorbeelden, zie deze referentiegids
Decorator
De meest voorkomende manier om bronnen te bespotten is om een python Decorator rond uw testfunctie:
@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123
in dit geval kan wat we patchen (thing
) een variabele of een functie zijn.,
Als u dit doet, moet u een argument doorgeven aan uw functie (U kunt het noemen wat u wilt †) dat een MagicMock
zal zijn.
Dit betekent dat als u niets anders doet, aanroepen naar thing
(tenminste in het voorbeeld hierboven) resulteren in de waarde 123
worden geretourneerd.
† conventie is om de variabele
mock_<noun>
te noemen.,
Als u meerdere dingen bespot, stapelt u de mock decorators op elkaar en geeft ze door om de testfunctie te gebruiken:
@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...
Resource location
Het is belangrijk om te weten dat u bij het bespotten de locatie van de te bespotten resource moet opgeven, relevant voor waar deze wordt geïmporteerd., Dit kan het beste worden uitgelegd door middel van een voorbeeld…
stel je voor dat ik een module app.foo
heb en binnen die module importeer ik een andere afhankelijkheid als volgt:
from app.bar import thing
je zou kunnen denken dat wanneer je mock.patch
dat je het een referentie geeft aan de bron zoals app.bar.thing
. Dat zou alleen relevant zijn als de bron werd aangeroepen met dat volledige pad binnen de app.foo
module (bijvoorbeeld als app.foo
genaamd app.bar.thing(...)
).,
als er niet naar het volledige naamruimtepad wordt verwezen, wat niet voorkomt in het bovenstaande voorbeeld (merk op dat we alleen de thing
bron importeren). Het betekent dat we de referentie-namespace moeten specificeren waar het geïmporteerd moet worden:
@mock.patch('app.foo.thing')
dus ook al bestaat thing
binnen app.bar
we specificeren app.foo.thing
als app.foo
is waar we het hebben geïmporteerd voor gebruik. Dit vangt de hele tijd mensen op.,
Mock return_value vs side_effect
Als uw functie een try/behalve rond, dan kunt u gebruik side_effect
om te zorgen dat de roeping van de functie te activeren is een Uitzondering als de geretourneerde waarde:
@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))
Opmerking: als u gebruikt
return_value=Exception('whoops')
vervolgens de mock zou de terugkeer van de string representatie van de Uitzondering eerder dan de uitzondering zoalsside_effect
doet.,methode op een bespotten object werd genoemd:De reden dat dit ingewikkelder is te wijten aan hoe een mock terug een nieuwe mock bij het openen van een woning op een mock:
De bovenstaande code fout:
AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)
Je moet ervoor zorgen dat u de tegeldemaking van de mock op het juiste moment:
Verifieer Uitzonderingen
Als we willen om te controleren of een stuk code genereert een
Exception
type wanneer we het nodig hebben, kunnen we mock specifieke middelen te gooien een uitzondering en vervolgens gebruikenpytest.raises
als context manager om de beller van onze code geverifieerd worden.,We kunnen beweringen tegen dit verwachte gedrag vangen en doen door eerst de bron te bespotten die we een uitzondering willen gooien en deze onze eigen nep-uitzondering te laten gooien met behulp van de parameter
side_effect
.vervolgens specificeren we het exacte uitzonderingstype dat we verwachten te worden verhoogd met
pytest.raises(T)
:opmerking: maak niet de fout om beweringen in de
with
context manager te plaatsen., Zodra de uitzondering wordt verhoogd door de functie die wordt aangeroepen binnen dewith
context manager, wordt alle code erna in het blok overgeslagen.Clearing lru_cache
als een functie die u wilt testen de
functools.lru_cache
decorator heeft toegepast, dan moet u rekening houden met het bespotten van de reactie van die functie, omdat deze in één test in de cache wordt opgeslagen en het resultaat in de cache wordt geretourneerd wanneer u de functie opnieuw aanroept om een ander gedrag te testen (en u waarschijnlijk zal verwarren wanneer u zie de onverwachte reactie).,om dit probleem op te lossen is heel eenvoudig omdat
lru_cache
extra functies biedt bij het decoreren van uw functies, Het biedt:
cache_info
cache_clear
De laatste (
cache_clear
) is wat u zou moeten aanroepen. Dit wordt hieronder aangetoond:opmerking: debuggen dit is niet altijd duidelijk., Later demonstreer ik hoe de ingebouwde
open
functie te mockeren, en in dat scenario stuitte ik op dit probleem, want hoewel ik niet de top level functie zelf bespotte (ik bespotte de aanroep naaropen
binnen), was de inhoud van het bestand dat werd geopend wat werd geretourneerd en gecached.Mock moduleniveau/globale variabelen
met een modulevariabele kunt u de waarde direct instellen of
mock.patch
gebruiken.,in het volgende voorbeeld hebben we de variabele
client_id
die een globale variabele is binnen deapp.aws
module die we importeren om elders in onze code te verwijzen:in het
mock.patch
voorbeeld, zijn er twee belangrijke dingen op te merken:
- we gebruiken
return_value
.- Er is geen mock instantie doorgegeven aan de testfunctie.,
Dit komt omdat we een variabele aanpassen en geen directe functie of ‘callable’, dus er is geen noodzaak om een mock door te geven aan de testfunctie (als je de waarde een paar keer binnen de test zelf wilt veranderen, dan zou je de variabele mockeren, maar niet onmiddellijk een waarde toewijzen aan de decorator).
Mock Instance Method
er zijn meerdere manieren om de spot te drijven met een instance methode., Een gemeenschappelijke aanpak is het gebruik van
mock.patch.object
, zoals:een Andere benadering is te bespotten de methode, zoals je zou doen bij een normale functie, maar u verwijst naar de methode via de classname:
een Ander (hoewel meer zwaar op de hand) aanpak voor het spotten van een instantie van een klasse-methode is om te profiteren van het feit dat een Mock terug een nieuwe mock bijvoorbeeld wanneer aangeroepen:
@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"
Opmerking: in het bovenstaande voorbeeld hebben we spotten de hele klas, dat is misschien niet wat je wilt. Zo niet, gebruik dan het vorige
mock.patch.object
voorbeeld.,de reden dat het bovenstaande voorbeeld werkt is omdat we
return_value
instellen op onze mock. Omdat dit eenMagicMock
elk attribuut waarnaar verwezen wordt geeft een nieuwe mock instantie (een functie of eigenschap die je aanroept op een mock hoeft niet te bestaan) en dus roepen wemade_up_function
op de geretourneerde mock, en op die nieuw aangemaakte mock zetten we de uiteindelijkereturn_value
naar123
.,maar zoals vermeld in de bovenstaande noot, kan deze aanpak een beetje te bot zijn afhankelijk van wat uw behoeften zijn (of het u iets uitmaakt of u een wat functionele klasse hebt of niet).
Mock Class Method
om een class method mock te maken is een vergelijkbare benadering om een instance methode te bespotten.,
Een aanpak kan zijn dat u bespotten de hele klas (maar nu heb je een minder
return_value
toewijzen aan):mock_class.ClassMethodName.return_value = "123"
Of nog beter je zouden bespotten, zoals elke normale functie, maar slechts verwijzen naar de methode via de klasse:
Mock Hele Klas
met het bespotten van een hele klas die je nodig hebt om de
return_value
om een nieuwe instantie van de klasse.,Zie andere class gerelateerde spottips hier
Mock Async Calls
Mock asynchrone code is waarschijnlijk het meest verwarrende aspect van mocking. Mijn ‘Ga naar’ oplossing zal ik eerst uitleggen, maar daarna zal ik delen een aantal alternatieve methoden die ik heb gezien en geprobeerd in het verleden.,
overweeg eerst deze asynchrone code in een
app.foo
module:import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)
als we de coroutine moeten mockeren
app.stuff.some_concurrent_function
, dan kunnen we dit oplossen door een functie aan te maken die als een coroutine werkt en deze in te stellen voor verschillende soorten reacties:opmerking: het voorbeeld gebruikt tornado voor het uitvoeren van een asynchrone test.,e alternatieven…
AsyncMock
Opmerking: deze maakt gebruik van het pakket
pytest-asyncio
om te helpen met het testen van asyncio codeLaten we beginnen met de code om te worden bespot…
import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y
Nu hier is hoe we willen mock het…
Aap Patch
MagicMock Subklasse
Async Inline-Functie
Mock Aanleg Types
Wanneer het bespotten van een object vindt u dat de mock vervangt het gehele object en kan dus de oorzaak tests om te slagen (of falen) in onverwachte manieren.,
Betekenis, als je een mock meer wilt maken zoals de concrete interface, dan zijn er twee manieren om dat te doen:
spec
wrap
We kunnen mock ‘ s
spec
functie om alle methoden/attributen van het object dat wordt bespot na te bootsen. Dit zorgt ervoor dat uw spotters dezelfde api hebben als de objecten die ze vervangen.Opmerking: Er is een striktere
spec_set
die eenAttributeError
zal verhogen.,Dit kan het best worden aangetoond met een voorbeeld:
de parameter
wrap
daarentegen stelt u in staat om de implementatie te ‘bespioneren’ en het gedrag ervan te beïnvloeden.,op een van de boven die spot met het hele object, niet slechts één methode:Mock ingebouwde open functie
Python ‘ s mock bibliotheek biedt een abstractie voor het spotten met de ingebouwde
open
functie een stuk eenvoudiger…De
create=True
param set opmock.patch
betekent dat demock.MagicMock
geretourneerd, zal automatisch het maken van alle kenmerken die zijn genoemd op de mock (dit is omdat deopen
functie zal proberen om toegang te krijgen tot veel verschillende dingen en het is makkelijker voor bespotten, bespotten alle informatie die voor u).,conclusie
daar zullen we eindigen. Hopelijk kan deze lijst met spottechnieken je door de meest complexe code heen helpen die getest moet worden. Laat me weten wat je ervan vindt op twitter.