• Einführung
  • unittest.,verspotten oder zu verspotten
  • Decorator
  • Resource location
  • Modell return_value vs side_effect
  • Modell Geschachtelte Aufrufe
  • stellen Sie sicher Exceptions
  • Clearing lru_cache
  • Modell-Modul-Ebene/Globale Variablen
  • Modell-Instanz-Methode
  • Modell-Klasse-Methode
  • Mock Gesamte Klasse
  • Modell Async Calls
  • Modell-Instance-Typen
  • Modell builtin open Funktion
  • Vertragsschluss

Einführung

Mocking Ressourcen beim schreiben von tests in Python kann verwirrend sein, wenn Sie nicht vertraut sind mit, solche Dinge tun., In diesem Beitrag werde ich verschiedene Aspekte des Verspottungscodes behandeln, die hoffentlich eine nützliche Ressource für diejenigen sind, die ein bisschen stecken bleiben.

Hinweis: In den Codebeispielen verwende ich pytest, aber das sollte größtenteils keine Rolle spielen.

unittest.mock oder mock

Um eine Ressource zu verspotten, benötigen wir zuerst das Modul mock, und dies ist unser erster Stolperstein: Welche Version benötigen wir? dh es gibt zwei und beide scheinen offiziell zu sein (mock und unittest.mock).,

Das Modul mock ist eine abwärtskompatible Bibliothek, die Sie von PyPy herunterladen können, wobei unittest.mock dasselbe ist, aber nur mit der von Ihnen verwendeten Python-Version kompatibel ist.,

In fast allen Fällen möchten Sie es folgendermaßen importieren:

import unittest.mock as mock

Weitere Beispiele finden Sie in diesem Referenzhandbuch

Decorator

Die häufigste Methode zum Verspotten von Ressourcen ist die Verwendung eines Python-Dekorators um Ihre Testfunktion:

@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123

In diesem Fall kann das, was wir patchen (thing), eine Variable oder eine Funktion sein.,

Wenn Sie dies tun, müssen Sie ein Argument an Ihre Funktion übergeben (Sie können es beliebig benennen†), das eine MagicMock.

Dies bedeutet, wenn Sie nichts anderes tun, führen Aufrufe von thing (zumindest im obigen Beispiel) dazu, dass der Wert 123 zurückgegeben wird.

† Konvention ist, die Variable mock_<noun>zu benennen.,

Wenn Sie mehrere Dinge verspotten, stapeln Sie die Scheindekorateure übereinander und geben sie an die Testfunktion weiter:

@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...

Ressourcenstandort

Es ist wichtig zu wissen, dass Sie beim Verspotten den Speicherort der zu verspottenden Ressource angeben sollten, der für den Importort relevant ist., Dies lässt sich am besten anhand eines Beispiels erklären…

Stellen Sie sich vor, ich habe ein Modul app.foo und in diesem Modul importiere ich eine andere Abhängigkeit wie folgt:

from app.bar import thing

Sie könnten denken, dass Sie beim Aufruf von mock.patch einen Verweis auf die Ressource übergeben, z. B. app.bar.thing. Dies wäre nur relevant, wenn die Ressource mit diesem vollständigen Pfad innerhalb des Moduls app.foo aufgerufen würde (z. B. wenn app.foo app.bar.thing(...)).,

Wenn auf den vollständigen Namespace-Pfad nicht verwiesen wird, was im obigen Beispiel nicht der Fall ist (beachten Sie, dass wir nur die Ressource thing importieren). Es bedeutet, dass wir den Referenznamespace angeben müssen, um zu verspotten, wo er importiert wird:

@mock.patch('app.foo.thing')

Obwohl thing in app.bar vorhanden ist, geben wir app.foo.thing an, da app.foo dort ist, wo wir ihn importiert haben verwenden. Das fängt die Leute die ganze Zeit ein.,

Mock return_value vs side_effect

Wenn Ihre Funktion einen try/except hat, können Sie side_effect verwenden, um den Aufruf der Funktion zu veranlassen, eine Ausnahme als zurückgegebenen Wert auszulösen:

@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))

Hinweis: Wenn Sie return_value=Exception('whoops') dann würde der Mock die Zeichenfolgendarstellung der Ausnahme zurückgeben, anstatt eine Ausnahme wie side_effect auszulösen.,methode für ein verspottetes Objekt wurde aufgerufen:

Der Grund, warum dies komplizierter werden kann, liegt darin, wie ein Mock einen neuen Mock zurückgibt, wenn er auf eine Eigenschaft in einem Mock zugreift:

Der obige Code wird Fehler:

AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)

Sie müssen sicherstellen, dass Sie den Mock zur richtigen Zeit bestätigen:

Ausnahmen überprüfen

Wenn wir überprüfen möchten, ob ein Teil des Codes ein Exception – Typ, wenn wir ihn benötigen, können wir bestimmte Ressourcen verspotten, um eine Ausnahme auszulösen, und dann pytest.raises als Kontextmanager um den Aufrufer unseres zu überprüfenden Codes verwenden.,

Wir können dieses erwartete Verhalten abfangen und Zusicherungen machen, indem wir zuerst die Ressource verspotten, die wir eine Ausnahme auslösen möchten, und sie dazu bringen, unsere eigene gefälschte Ausnahme mit dem Parameter side_effect.

Als nächstes geben wir den genauen Ausnahmetyp an, den wir erwarten, mit pytest.raises(T):

Hinweis: Machen Sie nicht den Fehler, Behauptungen in den Kontextmanager with zu setzen., Sobald die Ausnahme durch die Funktion ausgelöst wird, die im Kontextmanager with aufgerufen wird, wird der gesamte Code danach innerhalb des Blocks übersprungen.

ru_cache

Wenn auf eine Funktion, die Sie testen möchten, der Dekoratorfunctools.lru_cache angewendet wurde, müssen Sie die Antwort dieser Funktion verspotten, da sie in einem Test zwischengespeichert wird und das zwischengespeicherte Ergebnis zurückgegeben wird, wenn Sie die Funktion erneut aufrufen, um ein anderes Verhalten zu testen (und Sie wahrscheinlich verwirren, wenn Sie die unerwartete Antwort sehen).,

Um dieses Problem zu beheben, ist es sehr einfach, da lru_cache beim Dekorieren Ihrer Funktionen zusätzliche Funktionen bereitstellt:

  • cache_info
  • cache_clear

Letzteres (cache_clear div>) ist das, was Sie anrufen müssten. Dies wird unten gezeigt:

Hinweis: Das Debuggen ist nicht immer offensichtlich., Später demonstriere ich, wie man die eingebaute open Funktion verspottet, und in diesem Szenario stolperte ich über dieses Problem, denn obwohl ich die oberste Funktion selbst nicht verspottete (ich verspottete den Aufruf von open), wurde der Inhalt der Datei, die geöffnet wurde, zurückgegeben und zwischengespeichert.

Modulebene/Globale Variablen verspotten

Mit einer Modulvariablen können Sie den Wert entweder direkt festlegen oder mock.patchverwenden.,

Im folgenden Beispiel haben wir die Variable client_id, die eine globale Variable innerhalb des app.aws – Moduls ist, auf das wir importieren, um an anderer Stelle in unserem Code zu verweisen:

Im mock.patch – Beispiel gibt es zwei wichtige Dinge zu beachten:

  1. Wir verwenden return_value.
  2. Es wird keine Scheininstanz an die Testfunktion übergeben.,

Dies liegt daran, dass wir eine Variable und keine direkte Funktion oder „aufrufbar“ ändern, sodass kein Mock an die Testfunktion übergeben werden muss (wenn Sie den Wert einige Male innerhalb des Tests selbst ändern möchten, dann würden Sie die Variable verspotten, aber nicht sofort einen Wert im Dekorator zuweisen).

Mock-Instanzmethode

Es gibt mehrere Möglichkeiten, das Verspotten einer Instanzmethode zu erreichen., Ein gängiger Ansatz ist die Verwendung von mock.patch.object wie folgt:

Ein anderer Ansatz besteht darin, die Methode wie eine normale Funktion zu verspotten, aber Sie verweisen auf die Methode über den Klassennamen:

Ein anderer (wenn auch schwerfälliger) Ansatz zum Verspotten einer Klasseninstanzmethode besteht darin, die Tatsache zu nutzen, dass ein Mock eine neue Mock-Instanz zurückgibt, wenn er aufgerufen wird:

@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"

Hinweis: Im obigen Beispiel verspotten wir die gesamte Klasse, die möglicherweise nicht Ihren Wünschen entspricht. Wenn nicht, verwenden Sie stattdessen das vorherige Beispiel mock.patch.object.,

Der Grund, warum das obige Beispiel funktioniert, ist, dass wir return_value auf unseren mock setzen. Da dies eine MagicMock ist, gibt jedes Attribut, auf das verwiesen wird, eine neue Mock-Instanz zurück (eine Funktion oder Eigenschaft, die Sie auf einem mock aufrufen, muss nicht vorhanden sein), und so rufen wir made_up_function für den zurückgegebenen Mock auf, und für diesen neu erstellten Mock setzen wir die endgültige return_value auf 123.,

Aber wie in der obigen Anmerkung erwähnt, ist dieser Ansatz möglicherweise etwas zu stumpf, je nachdem, was Ihre Bedürfnisse sind (ob es Ihnen wichtig ist, ob Sie eine funktionierende Klasse haben oder nicht).

Mock Class Method

Um eine Klassenmethode zu verspotten, ist ein ähnlicher Ansatz zum Verspotten einer Instanzmethode.,

Ein Ansatz könnte sein, dass Sie die gesamte Klasse verspotten (aber jetzt müssen Sie eine weniger return_value zuweisen):

mock_class.ClassMethodName.return_value = "123"

Oder noch besser, Sie sollten es verspotten, wie Sie es tun würden jede normale Funktion, aber verweisen Sie einfach auf die Methode über die Klasse:

Verspotten Sie die gesamte Klasse

Um eine ganze Klasse zu verspotten, müssen Sie div id=“6f2aa2b896″> um eine neue Instanz der Klasse zu sein.,

Weitere klassenbezogene Spotttipps finden Sie hier

Verspotten Sie asynchrone Aufrufe

Das Verspotten von asynchronem Code ist wahrscheinlich der verwirrendste Aspekt des Verspottens. Meine Lösung „Gehe zu“ werde ich zuerst erklären, aber danach werde ich einige alternative Methoden teilen, die ich in der Vergangenheit gesehen und ausprobiert habe.,

Betrachten Sie zuerst diesen asynchronen Code innerhalb eines app.foo Moduls:

import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)

Wenn wir die Coroutine verspotten müssen app.stuff.some_concurrent_function, dann können wir dies lösen, indem wir eine Funktion erstellen, die als Coroutine fungiert und es für verschiedene Arten von Antworten konfigurierbar macht:

Hinweis: Im Beispiel wird tornado zum Ausführen eines asynchronen Tests verwendet.,e>…

AsyncMock

Hinweis: Dies verwendet das Paket pytest-asyncioum beim Testen des asyncio-Codes zu helfen

Beginnen wir mit dem zu verspottenden Code…

import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y

So würden wir es verspotten…

Monkey Patch

MagicMock Subclass

Async Inline Function

Mock Instance Types

Wenn Sie ein Objekt verspotten, werden Sie feststellen, dass der Mock das gesamte Objekt ersetzt und Tests auf unerwartete Weise bestehen (oder fehlschlagen) kann.,

Das heißt, wenn Sie ein Mock mehr wie die konkrete Schnittstelle machen müssen, dann gibt es zwei Möglichkeiten, das zu tun:

  1. spec
  2. wrap

Wir können Mocksspec feature verwenden, um alle Methoden/Attribute von das Objekt wird verspottet. Dies stellt sicher, dass Ihre Mocks die gleiche API wie die Objekte haben, die sie ersetzen.

Hinweis: Es gibt eine strengere spec_set, die eine AttributeError.,

Dies wird am besten anhand eines Beispiels demonstriert:

Mit dem Parameter wrap können Sie andererseits die Implementierung „ausspionieren“ und ihr Verhalten beeinflussen.,eines der oben genannten, das das gesamte Objekt verspottet, nicht nur eine einzige Methode:

Mock builtin open function

Pythons Mock-Bibliothek bietet eine Abstraktion zum Verspotten der eingebauten open – Funktion viel einfacher…

Die – Parameter, die auf mock.patch div id=“c3af3af75c“> returned erstellt automatisch alle Attribute, die auf dem mock aufgerufen werden (dies liegt daran, dass dieopen – Funktion versucht, auf viele verschiedene Dinge zuzugreifen, und es ist einfacher für mock, all das für Sie zu verspotten).,

Fazit

Dort enden wir. Hoffentlich kann diese Liste von Spotttechniken Sie auch durch den komplexesten Code führen, der getestet werden muss. Lassen Sie mich wissen, was Sie auf Twitter denken.