det är bara ett faktum av livet, eftersom koden växer så småningom måste du börja lägga till mocks till din testsvit. Det som började som ett gulligt litet tvåklassprojekt pratar nu med externa tjänster och du kan inte testa det bekvämt längre.
det är därför Python levereras medunittest.mock, en kraftfull del av standardbiblioteket för Stubb beroenden och gäckande biverkningar.
unittest.mock är dock inte särskilt intuitivt.,
Jag har funnit mig själv många gånger undrar varför min go-to recept inte fungerar för ett visst fall, så jag har satt ihop detta cheatsheet för att hjälpa mig själv och andra få hånar arbetar snabbt.
Du hittar kodexemplen i artikelns GitHub-arkiv. Jag kommer att använda Python 3.6, om du använder 3.2 eller under måste du använda mock PyPI-paketet.,
exemplen skrivs medunittest.TestCase klasser för enkelhet i att utföra dem utan beroenden, men du kan skriva dem som funktioner med pytest nästan direkt,unittest.mock fungerar bra. Om du är en pytest användare men jag uppmuntrar dig att ta en titt på det utmärkta pytest-mock biblioteket.
centerpunkten förunittest.mock – modulen är naturligtvisMock – klassen., Det viktigaste kännetecknet för en Mock objekt är att det kommer att returnera en annan Mock instans när:
åtkomst till en av dess attribut
anropa själva objektet
och en default som det första elementet i konstanten COUNTRIES. Logiken under test är att beroende på landet tillämpas en rabatt.,
för vilket vi kan skriva följande test:
men det skulle inte passera.,
låt oss gå igenom testet:
först patchar standardländerna i pricer för att vara en lista med en enda post GB ,
detta bör göra CountryPricer.country attributet standard till den posten eftersom det är definitionen inkluderar default=COUNTRIES
det sedan instanciates CountryPricer och ber om rabatterat pris för GB!
så vad händer?,
orsaken till detta ligger i Pythons beteende under importen, som bäst beskrivs i Luciano Ramalhos utmärkta flytande Python på kapitel 21:
för klasser är historien annorlunda: vid importtid utför tolken kroppen i varje klass, även kroppen av klasser kapslade i andra klasser. Utförande av en klasskropp innebär att klassens attribut och metoder definieras, och sedan byggs klassobjektet självt.,
tillämpa detta på vårt exempel:
country attributetsField – instans är byggd innan testet körs vid importtid,
Python läser klassens kropp och passerarCOUNTRIESCOUNTRIESField – instansen,
vår testkod körs men det är för sent för oss att lappa COUNTRIES och få ett korrekt påstående.,
från ovanstående beskrivning kan du försöka fördröja importen av modulen tills inuti testen, något som:
detta kommer dock fortfarande inte att passera som mock.patch importerar modulen och sedan monkeypatch den, resulterar i samma situation som tidigare.,
för att arbeta runt detta måste vi omfamna tillståndet för CountryPricer – klassen vid tidpunkten för testet och patchen default på den redan initierade Field – instansen:
den här lösningen är inte idealisk eftersom den kräver kunskap om de interna delarna av Field som du kanske inte har eller vill använda om du använder ett externt bibliotek men det fungerar på det här förenklade fallet.,
problemet med importtiden är ett av de största problemen jag har stött på när jag användeunittest.mock. Du måste komma ihåg när du använder den att vid importtidskoden på toppnivå av moduler exekveras, inklusive klasskroppar.
om logiken du testar är beroende av någon av denna logik kan du behöva tänka om hur du använder patch I enlighet därmed.,
inslagning
denna introduktion och cheatsheet ska få dig att håna med unittest.mock, men jag uppmuntrar dig att läsa dokumentationen noggrant, det finns gott om mer intressanta knep att lära.
det är värt att nämna att det finns alternativ tillunittest.mock, särskilt Alex Gaynorspretend bibliotek i kombination medpytest’smonkeypatch fixtur. Som Alex påpekar, med hjälp av dessa två bibliotek kan du ta ett annat tillvägagångssätt för att göra mocking strängare ännu mer förutsägbar., Definitivt ett tillvägagångssätt värt att utforska men utanför ramen för denna artikel.
unittest.mock är för närvarande standard för mocking i Python och du hittar den i nästan alla kodbaser. Att ha en solid förståelse för det hjälper dig att skriva bättre tester snabbare.