Wie Patch in Python?

Veröffentlicht in: Articles | 0

Was ist (Affen-)patching in Python?

(monkey-) patching ist eine Technik zum Ändern des Codeverhaltens, ohne dessen Quelle zu ändern. Dies geschieht zur Laufzeit, normalerweise durch Überschreiben von Attributen vorhandener Objekte. Ein Objekt kann eine Instanz einer Art sein, eine Klasse oder sogar ein Modul. Die Technik wird am häufigsten (ab)für Tests verwendet, wenn wir Mocks nicht auf einfache Weise bestehen können.

Ein weiteres beeindruckendes Beispiel ist die gevent-Bibliothek, die synchronen Code mithilfe von Monkey-Patching in asynchronen Code umwandelt.,

Vergleichen wir es mit wrk:

Gevent hat Anfragen zu einer Coroutine-freundlichen Bibliothek gemacht und dank der Parallelität konnte unser Beispielserver über 13,5 Mal mehr Anfragen pro Sekunde verarbeiten.

Am Ende haben wir ein Programm mit Coroutine-basierter Parallelität (dasselbe Prinzip wie in asyncio oder node.js), aber sein Code sieht immer noch wie synchron aus. Wir benötigen keine speziellen, kooperativen Bibliotheken oder async / Await-Schlüsselwörter in unserem Code. Es ist fast wie Magie.

Patch-tests

Python enthält ein Dienstprogramm für Patches, also unittest.verspotten.Patch., Die Standardmethode der Verwendung besteht darin, unsere Testfunktion zu dekorieren. Angenommen, wir haben eine Django-Ansicht, die so aussieht…

und wir möchten sie testen. Wir stellen fest, dass es eine Abhängigkeit hat – APIClient von einem anderen Modul. Wenn wir die get_stats-Ansicht vorhersehbar und zuverlässig testen möchten, müssen wir anstelle von APIClient ein test-double verwenden. Es gibt jedoch keinen einfachen Weg dazu. Wenn es als Argument an get_stats übergeben wurde, könnten wir stattdessen einfach Mock übergeben.,

1
2
3

/div>

4
def get_stats(request, api_client_class):
api_client = api_client_class()
…,

…aber das ist nicht der Fall. Wir können trotzdem Patch Decorator verwenden!

Dies ist noch nicht beendet, aber wenn wir einen Debugger in den Test einfügen, bemerken wir diesen APIClient.get_stats_for ist jetzt ein MagicMock:

Es bedeutet, dass unser Verspotten erfolgreich war. Wir haben eine problematische Abhängigkeit durch einen Mock ersetzt. Übrigens, wenn Sie nach Best Practices für die Verwendung von Mocks suchen, lesen Sie meine (fast) definitive Anleitung zum Verspotten in Python oder warum Verspotten bei Überbeanspruchung gefährlich sein kann.,

Jetzt schlägt der Test immer noch fehl, da get_stats einen MagicMock empfängt, während er ein Wörterbuch erwartet. Wir müssen den Mock parametrisieren. Wir können dies tun, indem wir ein zweites Argument an @patch:

Patch ohne Dekorator

patch kann auch als Kontextkrippe verwendet werden. Ein Rückgabeergebnis ist ein Mock, der an einer Stelle eingefügt wird, an der ein Attribut gepatcht wird:

“ Python Patch funktioniert nicht!“- wie man es richtig macht?

Manchmal sehen Sie sich der Situation gegenüber, in der die Abhängigkeit trotz des Vorhandenseins von Patch Decorator oder Context Manager so aussieht, als wäre sie überhaupt nicht gepatcht worden., Kurz gesagt, es kann daran liegen, dass mehrere Verweise auf die Sache vorhanden sind, die Sie patchen möchten. Der zu testende Code verwendet einen, aber Sie haben einen anderen erfolgreich gepatcht. Die operation war erfolgreich, aber der patient starb. Was zu tun?

Kurz gesagt, Sie müssen sicherstellen, dass Sie dieselbe Referenz patchen, die der zu testende Code verwendet.

Siehe Wo Patch-Abschnitt von unittest.mock-Dokumentation für weitere details. Alternativ können Sie eine raffinierte Alternative zu Patch verwenden, dh Patch.Objekt.

patch.Objekt – einfacher, es richtig zu machen

patch.,object ist sehr einfach zu verwenden – Sie importieren einfach das Objekt, dessen Attribut Sie patchen und patch anwenden möchten.objekt:

Wenn Sie Patch verwenden möchten.objekt für eine Methode importieren Sie eine Klasse. Wenn Sie patchen möchten.objekt eine Funktion oder ganze Klasse, importieren Sie das Modul, in dem sie leben.

Sollten Sie patchen?

(Affen-) Patchen sollte sparsam eingesetzt werden. Das sollte dein letzter Ausweg sein. In meinem Code habe ich dank Dependency Injection keine andere Option als Patch.

Langfristig ist der Preis für solche Tricks sehr, sehr hoch., Patching bedeutet oft, Implementierungsdetails auf eine Weise zu berühren und zu ändern, die von den Autoren nicht vorgesehen war. Dies führt zu einer zusätzlichen Kopplung mit Dingen, die es nicht haben sollten. Es bedeutet, dass sie schwieriger zu ändern sein werden.

Wenn Sie wirklich müssen, patchen Sie nur die öffentliche API einer anderen Bibliothek oder eines Moduls in Ihrem Code.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.