Python tentamen det er noe unintuitive om deg. Du er ikke lett å få tak i, og det tar flere «Ah, nå får jeg det!»et øyeblikk før du er bedre forstått., Du er en kraftig og fantastisk verktøy for å muliggjøre automatisert enhet tester, men jeg vet at mange utviklere som «bli med» på den kunnskapen de har, og å skrive disse testene tar lengre tid enn det burde.
Det er mange artikler om tentamen, og ja, jeg er nå, men skriver en annen. Men jeg virkelig ønsker å ha en gå på tydelig forklare det, spesielt de «Aha!»øyeblikk som presset frem min forståelse.
for det Første, hva er tentamen? Her er et sitat fra dokumenter:
mock er et bibliotek for testing i Python., Det tillater deg å bytte ut deler av systemet under test med mock-objekter og gjøre påstander om hvordan de har blitt brukt.
Så «det tillater deg å bytte ut deler av systemet, men hvilke deler? Det viser seg at du kan mock ganske mye hva du kan definere, for eksempel:
- funksjoner
- klasser
- objekter
Du erstatte dem med «mock-objekter» som er forekomster av Mock eller MagicMock klasse.,
Du kan deretter «gjør påstander» om at Mock eksempel, som er en måte å kontrollere at Mock eksempel ble brukt på den måten du forventer.
I denne artikkelen, jeg kommer til å dekke grunnleggende med å bytte ut deler av systemet, men jeg vil ikke være som dekker gjør påstander.
La oss starte med et enkelt eksempel. Jeg kommer til å opprette en ny modul kalt simple.py og i den modulen jeg kommer til å definere en funksjon som kalles simple_function som bare returnerer en streng som så:
def simple_function():
return "You have called simple_function"
Nå er jeg kommer til å lage en modul som heter use_simple.py., I denne modulen jeg kommer til å importere mock-pakke fra unittest og importere enkelt modul:
from unittest import mockimport simple
Neste jeg kommer til å skrive to funksjoner, en som kaller simple_function normalt og en som spotter kallet til det. Vær oppmerksom på at normalt ville du gjøre dette i en testing sammenheng, men jeg er målbevisst for at stripping bort slik at jeg kan fokusere på tentamen del. Den første funksjonen:
def use_simple_function():
result = simple.simple_function()
print(result)use_simple_function()
Som du kan se er det bare å skrive ut resultatet av simple_function., Spesielt, produksjonen er:
You have called simple_function
For den andre funksjonen, for å håne simple_function jeg kommer til å bruke narr.patch dekoratør. Dette decorator kan du angi hva du ønsker å håne ved å føre det en streng i formatet » – pakke.modul.FunctionName’., I vårt eksempel er det ingen pakken, men modulen kalles enkelt og funksjonen kalles simple_function, så dekoratør vil se slik ut:
@mock.patch('simple.simple_function')
Vi har da å skrive våre andre funksjonen som så:
@mock.patch('simple.simple_function')
def mock_simple_function():
jeg har kalt det mock_simple_function, men det kan ikke kalles noe. Vi mangler en parameter her fordi når du dekorere en funksjon med @narr.patch det vil passere en forekomst av MagicMock klasse (en MagicMock objekt) som brukes til å erstatte den funksjonen du er tentamen., Slik vil det se ut som dette:
@mock.patch('simple.simple_function')
def mock_simple_function(mock_simple_func):
Så ved å spesifisere @narr.patch («enkel.simple_function’) jeg sier jeg vil bytte simple_function med MagicMock objekt som er tilordnet til parameter kalt mock_simple_func.
Først la oss kjøttet ut funksjonen ved å skrive ut mock_simple_func og så kalle det:
Når du kjører, produksjonen er:
<MagicMock name='simple_function'>
Som en MagicMock objekt som vil bli kalt i stedet for simple_function., Det er viktig å merke seg navnet egenskap av objektet er det som blir spottet, og id er et unikt nummer for objektet.
Hvis vi nå også skrive ut simple_function (men ikke kalle det):
Som produserer utgang:
<MagicMock name='simple_function'>
<MagicMock name='simple_function'>
Du kan se det fra den matchende-ider som mock_simple_func er den samme MagicMock objekt som simple_function.
så langt Så bra. Vi har spottet simple_function. Å være tydelig, simple_function har blitt ertet fordi den har blitt erstattet med en MagicMock objekt.,
La oss legge til linjer i fra den første funksjonen:
Når du kjører, produksjonen er:
Som du kan se fra den tredje linjen i utgang når simple_function er egentlig heter det skaper en annen MagicMock objekt. Husk at siden vi har spottet simple_function, det er faktisk en MagicMock objekt, så når vi kaller simple_function vi faktisk kalle MagicMock objekt!
For meg, dette er hvor den forvirring setter inn.
- – Hvorfor er en ny MagicMock objekt skapt?
- Og hvordan i all verden kan du ringer et objekt likevel?, Du normalt få «TypeError: ‘****’ objektet er ikke callable» når du prøver.
La oss ta et minutt til å se nærmere på MagicMock.
MagicMock har magi i sitt navn fordi den har standard implementasjoner av de fleste av python magiske metoder. Hva er en python-magic metode, spør du? Det er alle spesielle python funksjoner som har dobbel understrek i starten og slutten av navnet. Du kan finne en liste over dem her. En av interesse her er __samtale__., Ringefunksjonen er beskrevet som:
Kalles når et objekt kalles som en funksjon
Så hvis en klasse implementerer denne funksjonen kan du opprette en instans av klassen, og deretter ringe det eksempel som en funksjon dvs. :
Derfor, når MagicMock kalles som en funksjon, den __samtale__ funksjonen kalles. MagicMock i implementeringen av denne funksjonen lager en ny lissom!
Så når vi kaller simple_function() en ny mock er opprettet og tilbake. Ikke bare det, men vi har konseptet foreldre og barn spotter., Når en mock skaper en annen blir foreldre og nylig opprettet barnet. Som barn mock kunne lage andre spotter så ender du opp med en heirarchy av mock-objekter.
Lære punkt 1: Når en MagicMock objekt kalles som en funksjon av standard oppretter og returnerer en ny MagicMock objekt.
Tilbake til vårt eksempel. Så langt har vi bare lo av simple_function men ikke gjort noe med det, annet enn å skrive ut MagicMocks som er opprettet som et resultat. Si at jeg ønsket å endre hva som ble returnert fra simple_function(), hvordan skulle jeg gjøre det?, Jeg ville gjøre det ved hjelp av return_value holderen for MagicMock som så (jeg også kommer til å fjerne de to første ut som de er ikke lenger nødvendig):
Når du kjører, produksjonen er:
You have mocked simple_function
Som du kan se når simple_function() kalles nå MagicMock return_value er returnert i stedet for å opprette en andre MagicMock objekt.
Lære punkt 2: for Å endre det som er tilbake når en MagicMock objekt kalles som en funksjon, sett return_value.
Hvis du ønsker å gjøre mer enn å forandre returverdien deretter kan du bruke MagicMock.side_effect funksjonen., Den lar deg angi en helt ny funksjon som vil bli kalt i stedet for den du er tentamen. La oss gjøre dette. For det første, vil jeg definere en ny funksjon som vil være den side_effect (merk at det er behov for å ha den samme parameteren angitt som funksjon du er tentamen):
def side_effect_function():
return "SKABLAM!"
Nå kan vi definere en ny funksjon som bruker denne bivirkningen som så:
Alt, er det samme som mock_simple_function videre opp bortsett fra at jeg er stille side_effect av mock_simple_func i stedet for return_value.,
output er:
SKABLAM!
En god use case for å bruke bivirkning hvis du ønsker å teste en feil flyt og derfor du ønsker å heve et unntak i testen. Jeg vil endre side_effect_function å heve en feil i stedet:
def side_effect_function():
raise FloatingPointError("A disastrous floating point error has occurred")
Nå er det utgang er (jeg fjernet en del av traceback for klarhet):
Traceback (most recent call last):
File "use_simple.py”, line 18, in side_effect_function
raise FloatingPointError("A disastrous floating point error has occurred”)
FloatingPointError: A disastrous floating point error has occurred
Lære punkt 3: Å gjøre mer enn bare å endre return_value av en MagicMock, sett side_effect.
Tentamen Klasser
Så langt har vi bare lo av en funksjon, men om hvordan en klasse?, La oss definere en klasse i simple.py kalles SimpleClass med en metode som kalles eksplodere:
class SimpleClass(object):
def explode(self):
return "KABOOM!"
Nå i use_simple.py la oss skrive en funksjon som bruker SimpleClass og deretter ringe funksjon:
def use_simple_class():
inst = simple.SimpleClass()
print(inst.explode())use_simple_class()
Når du kjører utgang er:
KABOOM!
la oss Nå gjøre definere en annen funksjon, og velger å håne den helhet av simple_class bruker @narr.patch dekoratør:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
Som når tentamen en funksjon, @narr.,patch dekoratør passerer en MagicMock objekt som erstatter den klassen du er tentamen i funksjon det er å dekorere. Den MagicMock objekt i dette tilfellet er tilordnet til argumentet mock_class.
For øyeblikket vil jeg bare skrive ut MagicMock objekt og deretter kjøre den funksjon:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)mock_simple_class()
Når du kjører utgang er:
<MagicMock name='SimpleClass'>
Som med en funksjon som gjør narr av en MagicMock objektet er opprettet og bestått i., Nå kan du skrive ut SimpleClass slik at du kan se at det har blitt ertet og erstattet med samme MagicMock objekt:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)
print(simple.SimpleClass)
output nå er:
<MagicMock name='SimpleClass'>
<MagicMock name='SimpleClass'>
La oss opprette en forekomst av SimpleClass dvs. samtale SimpleClass(), og deretter skrive det ut. Husk at siden vi har spottet SimpleClass, hva som faktisk vil skje er at vi vil kalle MagicMock objekt som en funksjon:
output er:
Slik som forventet, ringer SimpleClass (), og derfor kaller MagicMock objekt som en funksjon oppretter en ny MagicMock objekt.,
Opp til nå, gjør narr av en klasse har vært mye som gjør narr av en funksjon. Imidlertid, i virkeligheten når vi definerer en klasse vi vil da opprette objekter ved hjelp av denne klassen, og det er disse objektene mer ofte enn ikke det at vi virkelig ønsker å håne. Spørsmålet er, hvordan du gjør narr av en forekomst som er opprettet fra en klasse? Vel, når du gjør narr av en klasse, og følgelig en MagicMock objektet er opprettet, hvis du refererer til return_value på at MagicMock objekt opprettes en ny MagicMock objekt som representerer forekomst av klassen opprettet. Blimey! Det var en setning og en halv! Dette var definitivt en av mine «Aha!,»øyeblikk, men selv skriver det ned, ikke gjøre det klart. La oss skrive ut return_value slik at du kan se hva jeg mener:
output er:
Den tredje linjen er fra utskrift instansen av klassen opprettet, og den fjerde linjen er fra å skrive ut return_value av MagicMock objekt sendes til funksjonen. Som du kan se de refererer til den samme MagicMock objekt.
Lære punkt 4: Når du gjør narr av en klasse, en MagicMock objektet er opprettet. Når du oppretter en instans av klassen en ny MagicMock objektet er opprettet., Når du refererer til return_value av at klasse er MagicMock objekt får du den samme MagicMock objektet som ble opprettet da forekomst av klassen var opprettet.
«Ahhhhh, hvorfor er det viktig?!?!?»Jeg hører deg gråte!
«Det er viktig hvis du ønsker å håne den eksplodere funksjonen» jeg nedenfor i retur!
Det neste spørsmålet du må stille er: er den metoden jeg ønsker å håne en klasse metode eller en forekomst metode? Fordi hvordan du endrer return_value av metode vil være forskjellig avhengig av svaret.,
Hvis metoden var en klasse metoden, så ville du sett return_value som så:
MagicMockObject.ClassMethodName.return_value = "A delightful return value"
Men hvis det var en forekomst metode ville du gjøre:
MagicMockObject.return_value.InstanceMethodName.return_value = "A daring return value"
Med det i tankene, la oss endre return_value av eksplodere, som et eksempel metoden (som jeg kommer til å fjerne de fleste av utskrift av regnskapet for klarhet) :
I koden ovenfor, mock_class.return_value returnerer MagicMock objekt som representerer forekomst av SimpleClass.
mock_class.returverdi.,eksplodere returnerer MagicMock objekt som representerer eksplodere metode for forekomsten av SimpleClass.
Derfor sette return_value av mock_class.returverdi.eksplodere setter return_value av eksplodere metode for forekomsten av SimpleClass.
output er:
BOO!
Mye som når du gjør narr av en funksjon, kan du angi side_effect for metoder for klasser og objekter også.
jeg håper denne artikkelen hjulpet deg å forstå tentamen litt mer. Kan du anbefale det hvis du likte det og føler seg fri til å forlate et svar som jeg ville elske å høre fra deg.