• 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 MagicMockzal 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 zoals side_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 gebruiken pytest.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 de with 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 naar open 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.patchgebruiken.,

in het volgende voorbeeld hebben we de variabele client_id die een globale variabele is binnen de app.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:

  1. we gebruiken return_value.
  2. 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 een MagicMock 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 we made_up_function op de geretourneerde mock, en op die nieuw aangemaakte mock zetten we de uiteindelijke return_value naar 123.,

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 eenapp.foo module:

import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)

als we de coroutine moeten mockerenapp.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 code

Laten 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:

  1. spec
  2. 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 strikterespec_set die eenAttributeErrorzal 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 op mock.patch betekent dat de mock.MagicMock geretourneerd, zal automatisch het maken van alle kenmerken die zijn genoemd op de mock (dit is omdat de open 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.