Sådan køres enhedstest i Python uden at teste din tålmodighed
oftere end ikke interagerer den Soft .are, vi skriver, direkte med det, vi ville mærke som “beskidte” tjenester. I lægmandsbetingelser: tjenester, der er afgørende for vores applikation, men hvis interaktioner har tilsigtet, men uønskede bivirkninger-det vil sige uønsket i forbindelse med en autonom testkørsel.,facebook-facebook-funktion’, men ønsker ikke faktisk at sende til Facebook, hver gang vi kører vores testsuite.
Python unittest
bibliotek indeholder en sendt på kollisionskurs med månen opkaldt unittest.mock
—eller hvis du erklærer det som en afhængighed, blot mock
, som giver ekstremt stærkt og nyttigt middel til at håne og skodde disse uønskede bivirkninger.,
Bemærk: mock
er nyligt er inkluderet i standard-bibliotek for Python-3.3; forud distributioner bliver nødt til at bruge Mock bibliotek kan downloades via PyPI.
systemopkald vs. Python Mocking
for at give dig et andet eksempel, og et, som vi vil køre med for resten af artiklen, skal du overveje systemopkald., Det er ikke svært at se, at disse er de primære kandidater til spottende: uanset om du skriver et script for at skubbe en CD-drev, en web-server, som fjerner forældede cache-filer fra /tmp
, eller en socket-server, som binder sig til en TCP port, disse opkald, der alle har uønskede bivirkninger i forbindelse med din unit-tests.
Som udvikler er du mere interesseret i, at dit bibliotek med succes kaldte systemfunktionen til udstødning af en CD (med de rigtige argumenter osv.) i modsætning til faktisk at opleve din CD-bakke åben hver gang en test køres. (Eller værre, flere gange, da flere tests refererer til eject-koden under en enkelt enhed-testkørsel!)
ligeledes holder dine enhedstests effektive og effektive midler at holde så meget “langsom kode” ude af de automatiserede testkørsler, nemlig filsystem og netværksadgang.,
i vores første eksempel refactor vi en standard Python test case fra original form til en ved hjælp af mock
. Vi demonstrerer, hvordan skrivning af en testcase med mocks vil gøre vores test smartere, hurtigere og i stand til at afsløre mere om, hvordan soft .aren fungerer.
en simpel sletningsfunktion
Vi har alle brug for at slette filer fra vores filsystem fra tid til anden, så lad os skrive en funktion i Python, hvilket vil gøre det lidt lettere for vores scripts at gøre det.,
#!/usr/bin/env python# -*- coding: utf-8 -*-import osdef rm(filename): os.remove(filename)
Selvfølgelig kan vores rm
metode på dette tidspunkt, giver ikke meget mere end den underliggende os.remove
metode, men vores codebase vil forbedre, hvilket giver os mulighed for at tilføje mere funktionalitet her.
lad os skrive en traditionel testcase, dvs.uden mocks:
vores testcase er ret enkel, men hver gang den køres, oprettes en midlertidig fil og slettes derefter., Derudover har vi ingen måde at teste, om vores rm
metode korrekt passerer argumentet ned til os.remove
opkald. Vi kan antage, at det gør baseret på testen ovenfor, men meget er tilbage at ønske.
Refactoring med Python Håner
Lad os refactor vores test tilfælde ved hjælp af mock
:
Med disse refactors, har vi fundamentalt ændret den måde, at testen fungerer. Nu har vi en insider, et objekt, vi kan bruge til at verificere en anden funktionalitet.,
Potentielle Python Spottende Faldgruber
En af de første ting, der bør holde ud, er, at vi ved brug af mock.patch
metode, dekoratør at spotte et objekt placeret på mymodule.os
, og intravenøs at spotte i vores test-case metoden. Ville det ikke være mere fornuftigt at bare mock os
selv, snarere end henvisningen til det på mymodule.os
?
Nå, Python er noget af en lusket slange, når det kommer til import og styring af moduler., Ved kørsel harmymodule
modulet sit egetos
, som importeres til sit eget lokale anvendelsesområde i modulet. Så hvis vi mocker os
, vil vi ikke se virkningerne af mock i mymodule
modul.
mantraet, der skal gentages, er dette:
Mock et emne, hvor det bruges, ikke hvor det kom fra.,
Hvis du har brug for at håne tempfile
modul for myproject.app.MyElaborateClass
, er du sandsynligvis nødt til at anvende mock at myproject.app.tempfile
, som hvert modul holder sin egen import.
Med den faldgrube ud af vejen, lad os fortsætte med at spotte.
tilføjelse Validering til ‘rm’
rm
metode defineret tidligere er ganske forenklet. Vi vil gerne have det validere, at en sti findes, og er en fil, før bare blindt forsøger at fjerne det., Lad os refactor rm
for at være lidt smartere:
#!/usr/bin/env python# -*- coding: utf-8 -*-import osimport os.pathdef rm(filename): if os.path.isfile(filename): os.remove(filename)
stor. Lad os nu justere vores testcase for at holde dækningen op.
vores testparadigme er fuldstændigt ændret. Vi kan nu verificere og validere intern funktionalitet af metoder uden nogen bivirkninger.
Fil-Fjernelse som en Service med Mock Patch
indtil videre har vi kun arbejdet med at levere håner for funktioner, men ikke for metoder på objekter, eller tilfælde, hvor spotten er nødvendige for at sende parametre. Lad os først dække objektmetoder.,
Vi begynder med en refactor af rm
– metoden i en serviceklasse. Der er virkelig ikke et berettiget behov for i sig selv at indkapsle en så simpel funktion i et objekt, men det vil i det mindste hjælpe os med at demonstrere nøglebegreber i mock
. Lad os refactor:
du vil bemærke, at ikke meget har ændret sig i vores testtilfælde:
fantastisk, så vi ved nu, at RemovalService
fungerer som planlagt., Lad os skabe en anden tjeneste, der erklærer det som en afhængighed:
Da vi allerede har test dækning på det RemovalService
, vi kommer ikke til at validere indre funktionalitet af rm
metode i vores test af UploadService
. Snarere vil vi simpelthen teste (uden bivirkninger, selvfølgelig), at UploadService
kalder RemovalService.rm
-metoden, som vi kender “just worksorks” ” fra vores tidligere testcase.
Der er to måder at gøre dette på:
- Mock ud
RemovalService.rm
metoden selv., - levere en hånet forekomst i konstruktøren af
UploadService
.
da begge metoder ofte er vigtige i enhedstest, gennemgår vi begge.
Option 1: Spottende f.eks Metoder
mock
bibliotek har en særlig metode dekoratør for spottende objekt, f.eks metoder og egenskaber, den @mock.patch.object
dekoratør:
Store! Vi har valideret, at UploadService
med succes kalder vores instansens rm
metode. Bemærk noget interessant derinde?, Lappemekanismen erstattede faktiskrm
– metoden for alleRemovalService
forekomster i vores testmetode. Det betyder, at vi faktisk kan inspicere forekomsterne selv. Hvis du vil se mere, kan du prøve at droppe et breakpoint i din mocking-kode for at få en god fornemmelse for, hvordan lappemekanismen fungerer.
Mock Patch faldgrube: Dekoratørordre
Når du bruger flere dekoratører på dine testmetoder, er ordren vigtig, og det er lidt forvirrende. Grundlæggende, når du kortlægger dekoratører til metodeparametre, skal du arbejde baglæns., Overvej dette eksempel:
bemærk, hvordan vores parametre matches med dekoratørernes omvendte rækkefølge? Det er delvis på grund af den måde, Python fungerer på. Med flere metode, dekoratører, her er rækkefølgen af udførelsen i pseudocode:
patch_sys(patch_os(patch_os_path(test_something)))
Da patch til sys
er den yderste patch, vil det blive henrettet sidste, hvilket gør det til det sidste parameter i den egentlige test metode argumenter. Vær opmærksom på denne brønd, og brug en debugger, når du kører dine test for at sikre dig, at de rigtige parametre injiceres i den rigtige rækkefølge.,
Mulighed 2: Oprettelse af Mock-forekomster
i stedet for at håne den specifikke instansmetode, kunne vi i stedet bare levere en hånet instans tilUploadService
med sin konstruktør. Jeg foretrækker mulighed 1 ovenfor, da det er meget mere præcist, men der er mange tilfælde, hvor Mulighed 2 kan være effektiv eller nødvendig. Lad os refactor vores test igen:
I dette eksempel, vi har ikke engang haft til at lappe eventuelle funktionalitet, vi simpelthen skaber en auto-spec for RemovalService
class, og derefter indsprøjtes dette tilfælde i vores UploadService
til at validere funktionalitet.,
mock.create_autospec
– metoden skaber en funktionelt ækvivalent forekomst til den medfølgende klasse. Hvad dette betyder, praktisk talt, er, at når den returnerede instans interageres med, det vil rejse undtagelser, hvis det bruges på ulovlige måder. Mere specifikt, hvis en metode kaldes med det forkerte antal argumenter, vil der blive rejst en undtagelse. Dette er ekstremt vigtigt, da refactors sker. Når et bibliotek ændres, går testene i stykker, og det forventes. Uden at bruge en auto-spec, vil vores test stadig passere, selvom den underliggende implementering er brudt.,
Faldgrube: mock.Mock
og mock.MagicMock
Klasser
mock
bibliotek omfatter også to vigtige klasser, som de fleste af den indre funktionalitet er bygget på: mock.Mock
og mock.MagicMock
. Når du vælger at bruge en mock.Mock
instans, amock.MagicMock
instans eller en auto-spec, skal du altid bruge en auto-spec, da det hjælper med at holde dine tests fornuftige for fremtidige ændringer., Dette skyldes, atmock.Mock
ogmock.MagicMock
Accepter alle metodekald og ejendomstildelinger uanset den underliggende API. Overvej følgende use case:
class Target(object): def apply(value): return valuedef method(target, value): return target.apply(value)
Vi kan teste dette med en mock.Mock
eksempel som dette:
Denne logik synes fornuftigt, men lad os ændre Target.apply
metode til at tage flere parametre:
class Target(object): def apply(value, are_you_sure): if are_you_sure: return value else: return None
Igen køre din test, og du vil finde, at det stadig går. Det er fordi det ikke er bygget mod din faktiske API., Det er derfor, du bør altid bruge create_autospec
metode og autospec
parameter med @patch
og @patch.object
dekoratører.Facebook Facebook-API-opkald
for at afslutte, lad os skrive et mere anvendeligt eksempel i den virkelige verden python mock, et som vi nævnte i introduktionen: udstationering af en besked til Facebook. Vi skriver en dejlig indpakningsklasse og en tilsvarende testcase.,
Her er vores testcase, som kontrollerer, at vi sender beskeden uden faktisk at sende beskeden:
Som vi har set indtil videre, er det virkelig nemt at begynde at skrive smartere test med mock
i Python.
konklusion
Pythonsmock
bibliotek, hvis det er lidt forvirrende at arbejde med, er en spilskifter til enhedstest. Vi har demonstreret almindelige brugssager til at komme i gang med at bruge mock
i enhedstest, og forhåbentlig vil denne artikel hjælpe Python-udviklere med at overvinde de indledende forhindringer og skrive fremragende, testet kode.,