Wat is (monkey-)patchen in Python?
(monkey-) patching is een techniek om het gedrag van de code te veranderen zonder de bron ervan te veranderen. Het wordt gedaan in runtime, meestal door het overschrijven van attributen van bestaande objecten. Een object kan een instantie van een soort, een klasse of zelfs een module zijn. De techniek wordt meestal (ab) gebruikt voor tests wanneer we niet op een eenvoudige manier kunnen slagen voor de spot.
een ander indrukwekkend voorbeeld is gevent library die synchrone code in asynchroon omzet door monkey-patching te gebruiken.,
laten we het benchmarken met behulp van wrk:
Gevent maakte Verzoeken een coroutine-vriendelijke bibliotheek en dankzij concurrency kon onze voorbeeldserver meer dan 13,5 keer meer verzoeken per seconde verwerken.
uiteindelijk hebben we een programma dat coroutine-gebaseerde concurrency heeft (hetzelfde principe als in asyncio of node.js) maar de code ziet er nog steeds synchroon uit. We hebben geen speciale, coöperatieve bibliotheken of Async/wait zoekwoorden nodig in onze code. Het is bijna als magie.
Patch in tests
Python bevat een hulpprogramma voor patchen, dat wil zeggen unittest.bespotten.patch., De standaard manier om het te gebruiken is om onze testfunctie te versieren. Stel dat we een Django-weergave hebben die er zo uitziet …
en we willen het graag testen. We merken dat het een afhankelijkheid – ApiClient heeft van een andere module. Als we get_stats view op een voorspelbare, betrouwbare manier willen testen, moeten we een test-double gebruiken in plaats van ApiClient. Er is echter geen eenvoudige manier om dit te doen. Als het werd doorgegeven aan get_stats als een argument, konden we gewoon Pass Mock plaats.,
1
2
3
4
|
def get_stats(aanvraag, api_client_class):
…
api_client = api_client_class ()
…,
|
…maar dat is niet het geval. We kunnen nog steeds patch decorator gebruiken, hoewel!
Dit is nog niet afgelopen, maar als we een debugger in de test zetten, merken we dat ApiClient.get_stats_for is nu een MagicMock:
het betekent dat onze spot succesvol was. We hebben een problematische afhankelijkheid vervangen door een spot. By the way, als je op zoek bent naar best practices voor het gebruik van mocks, bekijk dan mijn (bijna) definitieve gids over bespotten in Python of waarom bespotten gevaarlijk kan zijn bij overmatig gebruik.,
nu faalt de test nog steeds omdat get_stats een MagicMock ontvangt terwijl het een woordenboek verwacht. We moeten de spot parametreren. We kunnen dit doen door een tweede argument door te geven aan @patch:
Patch zonder decorator
patch kan ook worden gebruikt als een context manger. Een return resultaat zal een Mock zijn die wordt ingevoegd op een plaats van een attribuut dat wordt gepatcht:
” Python patch werkt niet!”- hoe doe je het goed?
soms zult u geconfronteerd worden met de situatie dat ondanks de aanwezigheid van patch decorator of context manager, de afhankelijkheid eruit zal zien alsof het helemaal niet gepatcht is., Kortom, het kan zijn omdat er meerdere bestaande verwijzingen zijn naar het ding dat je probeert te patchen. De te testen code gebruikt er een, maar je hebt een andere succesvol gepatcht. De operatie was succesvol, maar de patiënt stierf. Wat te doen?
kortom, U moet ervoor zorgen dat u dezelfde referentie patcht die de code in de test gebruikt.
zie de sectie waar u de patch moet maken van unittest.mock documentatie voor meer details. Als alternatief kunt u een handig alternatief voor patch gebruiken, dat is patch.object.
patch.object-eenvoudiger om het goed te krijgen
patch.,object is doodeenvoudig te gebruiken-je importeert gewoon het object waarvan je het attribuut wilt patchen en past patch toe.object:
Als u patch wilt gebruiken.object voor een methode importeert u een klasse. Als je wilt patchen.object een functie of hele klasse, importeer de module waarin ze leven.
moet u patchen?
(monkey-) patching moet spaarzaam worden gebruikt. Dat zou je laatste redmiddel moeten zijn. In mijn code heb ik geen andere optie dan patch dankzij dependency injection.
Op lange termijn is de prijs voor dergelijke trucs zeer, zeer hoog., Patchen betekent vaak het aanraken en veranderen van implementatiedetails op een manier die niet voorzien was door de auteurs. Dit introduceert extra koppeling met dingen die het niet zouden moeten hebben. Het betekent dat ze moeilijker te veranderen zullen zijn.
als het echt nodig is, patch dan alleen publieke API van een andere bibliotheek of een module in je code.
Geef een reactie