unittest.mock — mock object library¶
Hinzugefügt in Version 3.3.
Quellcode: Lib/unittest/mock.py
unittest.mock ist eine Bibliothek zum Testen in Python. Sie ermöglicht es Ihnen, Teile Ihres zu testenden Systems durch Mock-Objekte zu ersetzen und Behauptungen darüber aufzustellen, wie sie verwendet wurden.
unittest.mock bietet eine Kernklasse Mock, die die Notwendigkeit beseitigt, eine Vielzahl von Stubs in Ihrer gesamten Testsuite zu erstellen. Nachdem Sie eine Aktion ausgeführt haben, können Sie Behauptungen darüber aufstellen, welche Methoden/Attribute verwendet wurden und mit welchen Argumenten sie aufgerufen wurden. Sie können auch Rückgabewerte festlegen und benötigte Attribute auf die übliche Weise setzen.
Zusätzlich bietet Mock einen patch()-Decorator, der das Patchen von Modul- und Klassenebenenattributen innerhalb des Gültigkeitsbereichs eines Tests handhabt, sowie sentinel zum Erstellen eindeutiger Objekte. Sehen Sie sich den Schnellleitfaden für einige Beispiele an, wie Sie Mock, MagicMock und patch() verwenden können.
Mock ist für die Verwendung mit unittest konzipiert und basiert auf dem Muster „Aktion -> Behauptung“ anstelle von „Aufzeichnen -> Wiedergeben“, das von vielen Mocking-Frameworks verwendet wird.
Es gibt einen Backport von unittest.mock für frühere Python-Versionen, verfügbar als mock auf PyPI.
Schnellleitfaden¶
Mock und MagicMock Objekte erstellen alle Attribute und Methoden, wenn Sie darauf zugreifen, und speichern Details darüber, wie sie verwendet wurden. Sie können sie konfigurieren, um Rückgabewerte festzulegen oder einzuschränken, welche Attribute verfügbar sind, und dann Behauptungen darüber aufstellen, wie sie verwendet wurden.
>>> from unittest.mock import MagicMock
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
side_effect ermöglicht es Ihnen, Nebeneffekte auszuführen, einschließlich des Auslösens einer Ausnahme, wenn ein Mock aufgerufen wird.
>>> from unittest.mock import Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()
Traceback (most recent call last):
...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect(arg):
... return values[arg]
...
>>> mock.side_effect = side_effect
>>> mock('a'), mock('b'), mock('c')
(1, 2, 3)
>>> mock.side_effect = [5, 4, 3, 2, 1]
>>> mock(), mock(), mock()
(5, 4, 3)
Mock bietet viele weitere Möglichkeiten zur Konfiguration und Steuerung seines Verhaltens. Zum Beispiel konfiguriert das Argument spec den Mock so, dass er seine Spezifikation von einem anderen Objekt übernimmt. Der Versuch, auf Attribute oder Methoden des Mocks zuzugreifen, die auf dem Spec nicht vorhanden sind, führt zu einem AttributeError.
Der patch()-Decorator / Kontextmanager erleichtert das temporäre Patchen von Klassen oder Objekten in einem zu testenden Modul. Das von Ihnen angegebene Objekt wird während des Tests durch einen Mock (oder ein anderes Objekt) ersetzt und am Ende des Tests wiederhergestellt.
>>> from unittest.mock import patch
>>> @patch('module.ClassName2')
... @patch('module.ClassName1')
... def test(MockClass1, MockClass2):
... module.ClassName1()
... module.ClassName2()
... assert MockClass1 is module.ClassName1
... assert MockClass2 is module.ClassName2
... assert MockClass1.called
... assert MockClass2.called
...
>>> test()
Hinweis
Wenn Sie Patch-Decorator verschachteln, werden die Mocks in derselben Reihenfolge an die dekorierte Funktion übergeben, in der sie angewendet wurden (die normale Python-Reihenfolge der Decorator-Anwendung). Das bedeutet von unten nach oben. Im obigen Beispiel wird der Mock für module.ClassName1 zuerst übergeben.
Bei patch() ist es wichtig, dass Sie Objekte in dem Namespace patchen, in dem sie nachgeschlagen werden. Dies ist normalerweise unkompliziert, aber für einen schnellen Überblick lesen Sie wo zu patchen.
Neben einem Decorator kann patch() als Kontextmanager in einer `with`-Anweisung verwendet werden.
>>> with patch.object(ProductionClass, 'method', return_value=None) as mock_method:
... thing = ProductionClass()
... thing.method(1, 2, 3)
...
>>> mock_method.assert_called_once_with(1, 2, 3)
Es gibt auch patch.dict() zum Setzen von Werten in einem Wörterbuch nur für einen bestimmten Gültigkeitsbereich und zum Wiederherstellen des Wörterbuchs in seinen ursprünglichen Zustand, wenn der Test endet.
>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
... assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original
Mock unterstützt das Mocking von Python Magic-Methoden. Der einfachste Weg, Magic-Methoden zu verwenden, ist die Klasse MagicMock. Sie ermöglicht Ihnen Dinge wie
>>> mock = MagicMock()
>>> mock.__str__.return_value = 'foobarbaz'
>>> str(mock)
'foobarbaz'
>>> mock.__str__.assert_called_with()
Mock ermöglicht es Ihnen, Funktionen (oder andere Mock-Instanzen) an Magic-Methoden zuzuweisen, und diese werden entsprechend aufgerufen. Die Klasse MagicMock ist nur eine Mock-Variante, die alle Magic-Methoden für Sie vorbereitet hat (nun ja, zumindest alle nützlichen).
Das Folgende ist ein Beispiel für die Verwendung von Magic-Methoden mit der normalen Mock-Klasse.
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='wheeeeee')
>>> str(mock)
'wheeeeee'
Um sicherzustellen, dass die Mock-Objekte in Ihren Tests dieselbe API haben wie die Objekte, die sie ersetzen, können Sie Auto-Speccing verwenden. Auto-Speccing kann über das Argument autospec für patch oder die Funktion create_autospec() erfolgen. Auto-Speccing erstellt Mock-Objekte, die dieselben Attribute und Methoden wie die zu ersetzenden Objekte haben, und alle Funktionen und Methoden (einschließlich Konstruktoren) haben dieselbe Aufrufsignatur wie das reale Objekt.
Dadurch wird sichergestellt, dass Ihre Mocks auf die gleiche Weise fehlschlagen wie Ihr Produktionscode, wenn sie falsch verwendet werden.
>>> from unittest.mock import create_autospec
>>> def function(a, b, c):
... pass
...
>>> mock_function = create_autospec(function, return_value='fishy')
>>> mock_function(1, 2, 3)
'fishy'
>>> mock_function.assert_called_once_with(1, 2, 3)
>>> mock_function('wrong arguments')
Traceback (most recent call last):
...
TypeError: missing a required argument: 'b'
create_autospec() kann auch auf Klassen angewendet werden, wo es die Signatur der `__init__`-Methode kopiert, und auf aufrufbare Objekte, wo es die Signatur der `__call__`-Methode kopiert.
Die Mock-Klasse¶
Mock ist ein flexibles Mock-Objekt, das den Einsatz von Stubs und Test-Doubles in Ihrem Code ersetzen soll. Mocks sind aufrufbar und erstellen Attribute als neue Mocks, wenn Sie darauf zugreifen [1]. Der Zugriff auf dasselbe Attribut gibt immer denselben Mock zurück. Mocks zeichnen auf, wie Sie sie verwenden, was es Ihnen ermöglicht, Behauptungen darüber aufzustellen, was Ihr Code mit ihnen getan hat.
MagicMock ist eine Unterklasse von Mock mit allen Magic-Methoden, die vorab erstellt und einsatzbereit sind. Es gibt auch nicht-aufrufbare Varianten, die nützlich sind, wenn Sie Objekte mocken, die nicht aufrufbar sind: NonCallableMock und NonCallableMagicMock.
Der patch()-Decorator erleichtert das temporäre Ersetzen von Klassen in einem bestimmten Modul durch ein Mock-Objekt. Standardmäßig erstellt patch() ein MagicMock für Sie. Sie können eine alternative Mock-Klasse mit dem Argument new_callable für patch() angeben.
- class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
Erstellt ein neues
Mock-Objekt.Mockakzeptiert mehrere optionale Argumente, die das Verhalten des Mock-Objekts spezifizieren.spec: Dies kann entweder eine Liste von Zeichenketten oder ein vorhandenes Objekt (eine Klasse oder Instanz) sein, das als Spezifikation für das Mock-Objekt dient. Wenn Sie ein Objekt übergeben, wird eine Liste von Zeichenketten gebildet, indem `dir` für das Objekt aufgerufen wird (unter Ausschluss nicht unterstützter Magic-Attribute und -Methoden). Der Zugriff auf jedes Attribut, das nicht in dieser Liste enthalten ist, löst einen
AttributeErroraus.Wenn spec ein Objekt (anstatt einer Liste von Zeichenketten) ist, gibt
__class__die Klasse des Spec-Objekts zurück. Dies ermöglicht es Mocks,isinstance()-Tests zu bestehen.spec_set: Eine strengere Variante von spec. Wenn verwendet, löst der Versuch, ein Attribut auf dem Mock zu *setzen* oder abzurufen, das nicht auf dem Objekt vorhanden ist, das als spec_set übergeben wurde, einen
AttributeErroraus.side_effect: Eine Funktion, die jedes Mal aufgerufen wird, wenn der Mock aufgerufen wird. Siehe das Attribut
side_effect. Nützlich zum Auslösen von Ausnahmen oder zum dynamischen Ändern von Rückgabewerten. Die Funktion wird mit denselben Argumenten wie der Mock aufgerufen, und sofern sie nichtDEFAULTzurückgibt, wird der Rückgabewert dieser Funktion als Rückgabewert verwendet.Alternativ kann side_effect eine Ausnahmeklasse oder -instanz sein. In diesem Fall wird die Ausnahme ausgelöst, wenn der Mock aufgerufen wird.
Wenn side_effect eine Iterable ist, gibt jeder Aufruf des Mocks den nächsten Wert aus dem Iterable zurück.
Eine side_effect kann durch Setzen auf `None` gelöscht werden.
return_value: Der Wert, der zurückgegeben wird, wenn der Mock aufgerufen wird. Standardmäßig ist dies ein neuer Mock (erstellt beim ersten Zugriff). Siehe das Attribut
return_value.unsafe: Standardmäßig löst der Zugriff auf jedes Attribut, dessen Name mit assert, assret, asert, aseert oder assrt beginnt, einen
AttributeErroraus. Das Übergeben von `unsafe=True` erlaubt den Zugriff auf diese Attribute.Hinzugefügt in Version 3.5.
wraps: Element, das der Mock umschließen soll. Wenn wraps nicht `None` ist, wird der Aufruf des Mocks an das umschlossene Objekt weitergeleitet (und gibt das tatsächliche Ergebnis zurück). Der Attributzugriff auf den Mock gibt ein Mock-Objekt zurück, das das entsprechende Attribut des umschlossenen Objekts umschließt (daher löst der Versuch, auf ein nicht vorhandenes Attribut zuzugreifen, einen
AttributeErroraus).Wenn der Mock einen expliziten return_value hat, werden Aufrufe nicht an das umschlossene Objekt weitergeleitet und stattdessen der return_value zurückgegeben.
name: Wenn der Mock einen Namen hat, wird dieser im repr des Mocks verwendet. Dies kann für Debugging-Zwecke nützlich sein. Der Name wird an untergeordnete Mocks weitergegeben.
Mocks können auch mit beliebigen Schlüsselwortargumenten aufgerufen werden. Diese werden verwendet, um Attribute auf dem Mock nach dessen Erstellung zu setzen. Details finden Sie in der Methode
configure_mock().- assert_called()¶
Behauptet, dass der Mock mindestens einmal aufgerufen wurde.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called()
Hinzugefügt in Version 3.6.
- assert_called_once()¶
Behauptet, dass der Mock genau einmal aufgerufen wurde.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_once() Traceback (most recent call last): ... AssertionError: Expected 'method' to have been called once. Called 2 times. Calls: [call(), call()].
Hinzugefügt in Version 3.6.
- assert_called_with(*args, **kwargs)¶
Diese Methode ist eine bequeme Möglichkeit, zu behaupten, dass der letzte Aufruf auf eine bestimmte Weise erfolgt ist.
>>> mock = Mock() >>> mock.method(1, 2, 3, test='wow') <Mock name='mock.method()' id='...'> >>> mock.method.assert_called_with(1, 2, 3, test='wow')
- assert_called_once_with(*args, **kwargs)¶
Behauptet, dass der Mock genau einmal aufgerufen wurde und dieser Aufruf mit den angegebenen Argumenten erfolgte.
>>> mock = Mock(return_value=None) >>> mock('foo', bar='baz') >>> mock.assert_called_once_with('foo', bar='baz') >>> mock('other', bar='values') >>> mock.assert_called_once_with('other', bar='values') Traceback (most recent call last): ... AssertionError: Expected 'mock' to be called once. Called 2 times. Calls: [call('foo', bar='baz'), call('other', bar='values')].
- assert_any_call(*args, **kwargs)¶
Behauptet, dass der Mock mit den angegebenen Argumenten aufgerufen wurde.
Die Behauptung ist erfolgreich, wenn der Mock *jemals* aufgerufen wurde, im Gegensatz zu
assert_called_with()undassert_called_once_with(), die nur erfolgreich sind, wenn der Aufruf der letzte ist, und im Fall vonassert_called_once_with()muss es auch der einzige Aufruf sein.>>> mock = Mock(return_value=None) >>> mock(1, 2, arg='thing') >>> mock('some', 'thing', 'else') >>> mock.assert_any_call(1, 2, arg='thing')
- assert_has_calls(calls, any_order=False)¶
Behauptet, dass der Mock mit den angegebenen Aufrufen aufgerufen wurde. Die Liste
mock_callswird auf die Aufrufe überprüft.Wenn any_order falsch ist, müssen die Aufrufe sequenziell sein. Es können zusätzliche Aufrufe vor oder nach den angegebenen Aufrufen vorhanden sein.
Wenn any_order wahr ist, können die Aufrufe in beliebiger Reihenfolge erfolgen, aber sie müssen alle in
mock_callserscheinen.>>> mock = Mock(return_value=None) >>> mock(1) >>> mock(2) >>> mock(3) >>> mock(4) >>> calls = [call(2), call(3)] >>> mock.assert_has_calls(calls) >>> calls = [call(4), call(2), call(3)] >>> mock.assert_has_calls(calls, any_order=True)
- assert_not_called()¶
Behauptet, dass der Mock niemals aufgerufen wurde.
>>> m = Mock() >>> m.hello.assert_not_called() >>> obj = m.hello() >>> m.hello.assert_not_called() Traceback (most recent call last): ... AssertionError: Expected 'hello' to not have been called. Called 1 times. Calls: [call()].
Hinzugefügt in Version 3.5.
- reset_mock(*, return_value=False, side_effect=False)¶
Die Methode `reset_mock` setzt alle Aufrufattribute eines Mock-Objekts zurück.
>>> mock = Mock(return_value=None) >>> mock('hello') >>> mock.called True >>> mock.reset_mock() >>> mock.called False
Dies kann nützlich sein, wenn Sie eine Reihe von Behauptungen machen möchten, die dasselbe Objekt wiederverwenden.
Der Parameter return_value setzt, wenn er auf `True` gesetzt ist,
return_valuezurück.>>> mock = Mock(return_value=5) >>> mock('hello') 5 >>> mock.reset_mock(return_value=True) >>> mock('hello') <Mock name='mock()' id='...'>
Der Parameter side_effect setzt, wenn er auf `True` gesetzt ist,
side_effectzurück.>>> mock = Mock(side_effect=ValueError) >>> mock('hello') Traceback (most recent call last): ... ValueError >>> mock.reset_mock(side_effect=True) >>> mock('hello') <Mock name='mock()' id='...'>
Beachten Sie, dass
reset_mock()standardmäßig wederreturn_valuenochside_effectoder andere Kindattribute, die Sie durch normale Zuweisung gesetzt haben, löscht.Kind-Mocks werden ebenfalls zurückgesetzt.
Geändert in Version 3.6: Zwei schlüsselwortbasierte Parameter wurden zur Funktion `reset_mock` hinzugefügt.
- mock_add_spec(spec, spec_set=False)¶
Fügt einem Mock einen Spec hinzu. spec kann entweder ein Objekt oder eine Liste von Zeichenketten sein. Nur Attribute im spec können als Attribute vom Mock abgerufen werden.
Wenn spec_set wahr ist, können nur Attribute im Spec gesetzt werden.
- attach_mock(mock, attribute)¶
Hängt einen Mock als Attribut des aktuellen Mocks an und ersetzt dabei dessen Namen und Elternteil. Aufrufe an den angehängten Mock werden in den Attributen
method_callsundmock_callsdieses Mocks aufgezeichnet.
- configure_mock(**kwargs)¶
Setzt Attribute auf dem Mock über Schlüsselwortargumente.
Attribute sowie Rückgabewerte und Nebeneffekte können auf untergeordneten Mocks über die normale Punktnotation und durch Entpacken eines Dictionaries im Methodenaufruf gesetzt werden.
>>> mock = Mock() >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs) >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
Dasselbe kann im Konstruktoraufruf für Mocks erreicht werden.
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock = Mock(some_attribute='eggs', **attrs) >>> mock.some_attribute 'eggs' >>> mock.method() 3 >>> mock.other() Traceback (most recent call last): ... KeyError
configure_mock()existiert, um die Konfiguration nach der Erstellung des Mocks zu vereinfachen.
- __dir__()¶
Mock-Objekte beschränken die Ergebnisse von `dir(some_mock)` auf nützliche Ergebnisse. Für Mocks mit einem spec umfasst dies alle zulässigen Attribute für den Mock.Siehe
FILTER_DIR, um zu erfahren, was diese Filterung bewirkt und wie sie ausgeschaltet werden kann.
- _get_child_mock(**kw)¶
Erstellt die untergeordneten Mocks für Attribute und Rückgabewerte. Standardmäßig sind untergeordnete Mocks vom gleichen Typ wie die Eltern. Unterklassen von Mock möchten dies möglicherweise überschreiben, um die Art und Weise, wie untergeordnete Mocks erstellt werden, anzupassen.
Für nicht-aufrufbare Mocks wird die aufrufbare Variante verwendet (anstelle einer benutzerdefinierten Unterklasse).
- called¶
Ein boolescher Wert, der angibt, ob das Mock-Objekt aufgerufen wurde oder nicht.
>>> mock = Mock(return_value=None) >>> mock.called False >>> mock() >>> mock.called True
- call_count¶
Eine Ganzzahl, die angibt, wie oft das Mock-Objekt aufgerufen wurde.
>>> mock = Mock(return_value=None) >>> mock.call_count 0 >>> mock() >>> mock() >>> mock.call_count 2
- return_value¶
Setzen Sie dies, um den Wert zu konfigurieren, der beim Aufrufen des Mocks zurückgegeben wird.
>>> mock = Mock() >>> mock.return_value = 'fish' >>> mock() 'fish'
Der Standardrückgabewert ist ein neuer Mock (erstellt beim ersten Zugriff) und Sie können ihn auf die übliche Weise konfigurieren.
>>> mock = Mock() >>> mock.return_value.attribute = sentinel.Attribute >>> mock.return_value() <Mock name='mock()()' id='...'> >>> mock.return_value.assert_called_with()
return_valuekann auch im Konstruktor gesetzt werden.>>> mock = Mock(return_value=3) >>> mock.return_value 3 >>> mock() 3
- side_effect¶
Dies kann entweder eine Funktion sein, die aufgerufen wird, wenn der Mock aufgerufen wird, ein Iterable oder eine Ausnahme (Klasse oder Instanz), die ausgelöst werden soll.
Wenn Sie eine Funktion übergeben, wird diese mit denselben Argumenten wie der Mock aufgerufen, und sofern die Funktion nicht
DEFAULTzurückgibt, gibt der Aufruf des Mocks dann zurück, was auch immer die Funktion zurückgibt. Wenn die FunktionDEFAULTzurückgibt, gibt der Mock seinen normalen Wert zurück (ausreturn_value).Wenn Sie ein Iterable übergeben, wird es verwendet, um einen Iterator abzurufen, der bei jedem Aufruf einen Wert liefern muss. Dieser Wert kann entweder eine Ausnahmeinstanz zum Auslösen sein oder ein Wert, der vom Aufruf des Mocks zurückgegeben wird (`DEFAULT`-Handling ist identisch zum Funktionsfall).
Ein Beispiel für einen Mock, der eine Ausnahme auslöst (um die Ausnahmebehandlung einer API zu testen).
>>> mock = Mock() >>> mock.side_effect = Exception('Boom!') >>> mock() Traceback (most recent call last): ... Exception: Boom!
Verwendung von
side_effect, um eine Sequenz von Werten zurückzugeben.>>> mock = Mock() >>> mock.side_effect = [3, 2, 1] >>> mock(), mock(), mock() (3, 2, 1)
Verwendung einer aufrufbaren Funktion.
>>> mock = Mock(return_value=3) >>> def side_effect(*args, **kwargs): ... return DEFAULT ... >>> mock.side_effect = side_effect >>> mock() 3
side_effectkann im Konstruktor gesetzt werden. Hier ist ein Beispiel, das einen Wert zum Wert hinzufügt, mit dem der Mock aufgerufen wird, und ihn zurückgibt.>>> side_effect = lambda value: value + 1 >>> mock = Mock(side_effect=side_effect) >>> mock(3) 4 >>> mock(-8) -7
Das Setzen von
side_effectauf `None` löscht es.>>> m = Mock(side_effect=KeyError, return_value=3) >>> m() Traceback (most recent call last): ... KeyError >>> m.side_effect = None >>> m() 3
- call_args¶
Dies ist entweder `None` (wenn der Mock nicht aufgerufen wurde) oder die Argumente, mit denen der Mock zuletzt aufgerufen wurde. Dies liegt in Form eines Tupels vor: Das erste Element, auf das auch über die Eigenschaft `args` zugegriffen werden kann, sind alle positionsbezogenen Argumente, mit denen der Mock aufgerufen wurde (oder ein leeres Tupel), und das zweite Element, auf das auch über die Eigenschaft `kwargs` zugegriffen werden kann, sind alle Schlüsselwortargumente (oder ein leeres Dictionary).
>>> mock = Mock(return_value=None) >>> print(mock.call_args) None >>> mock() >>> mock.call_args call() >>> mock.call_args == () True >>> mock(3, 4) >>> mock.call_args call(3, 4) >>> mock.call_args == ((3, 4),) True >>> mock.call_args.args (3, 4) >>> mock.call_args.kwargs {} >>> mock(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args call(3, 4, 5, key='fish', next='w00t!') >>> mock.call_args.args (3, 4, 5) >>> mock.call_args.kwargs {'key': 'fish', 'next': 'w00t!'}
call_args, zusammen mit den Elementen der Listencall_args_list,method_callsundmock_calls, sindcall-Objekte. Dies sind Tupel, so dass sie entpackt werden können, um auf einzelne Argumente zuzugreifen und komplexere Behauptungen aufzustellen. Siehe Aufrufe als Tupel.Geändert in Version 3.8: Die Eigenschaften `args` und `kwargs` wurden hinzugefügt.
- call_args_list¶
Dies ist eine Liste aller Aufrufe, die nacheinander an das Mock-Objekt gemacht wurden (daher ist die Länge der Liste die Anzahl der Aufrufe). Bevor irgendwelche Aufrufe getätigt wurden, ist es eine leere Liste. Das
call-Objekt kann verwendet werden, um bequem Listen von Aufrufen zu erstellen, die mitcall_args_listverglichen werden.>>> mock = Mock(return_value=None) >>> mock() >>> mock(3, 4) >>> mock(key='fish', next='w00t!') >>> mock.call_args_list [call(), call(3, 4), call(key='fish', next='w00t!')] >>> expected = [(), ((3, 4),), ({'key': 'fish', 'next': 'w00t!'},)] >>> mock.call_args_list == expected True
Elemente von
call_args_listsindcall-Objekte. Diese können als Tupel entpackt werden, um auf einzelne Argumente zuzugreifen. Siehe Aufrufe als Tupel.
- method_calls¶
Neben der Verfolgung von Aufrufen an sich selbst verfolgen Mocks auch Aufrufe an Methoden und Attribute sowie an deren Methoden und Attribute.
>>> mock = Mock() >>> mock.method() <Mock name='mock.method()' id='...'> >>> mock.property.method.attribute() <Mock name='mock.property.method.attribute()' id='...'> >>> mock.method_calls [call.method(), call.property.method.attribute()]
Elemente von
method_callssindcall-Objekte. Diese können als Tupel entpackt werden, um auf einzelne Argumente zuzugreifen. Siehe Aufrufe als Tupel.
- mock_calls¶
mock_callszeichnet *alle* Aufrufe an das Mock-Objekt, seine Methoden, Magic-Methoden *und* Rückgabewert-Mocks auf.>>> mock = MagicMock() >>> result = mock(1, 2, 3) >>> mock.first(a=3) <MagicMock name='mock.first()' id='...'> >>> mock.second() <MagicMock name='mock.second()' id='...'> >>> int(mock) 1 >>> result(1) <MagicMock name='mock()()' id='...'> >>> expected = [call(1, 2, 3), call.first(a=3), call.second(), ... call.__int__(), call()(1)] >>> mock.mock_calls == expected True
Mitglieder von
mock_callssindcallObjekte. Diese können als Tupel entpackt werden, um an die einzelnen Argumente zu gelangen. Siehe Aufrufe als Tupel.Hinweis
Die Art und Weise, wie
mock_callsaufgezeichnet werden, bedeutet, dass bei verschachtelten Aufrufen die Parameter von Elternaufrufen nicht aufgezeichnet werden und daher immer gleich verglichen werden.>>> mock = MagicMock() >>> mock.top(a=3).bottom() <MagicMock name='mock.top().bottom()' id='...'> >>> mock.mock_calls [call.top(a=3), call.top().bottom()] >>> mock.mock_calls[-1] == call.top(a=-1).bottom() True
- __class__¶
Normalerweise gibt das Attribut
__class__eines Objekts seinen Typ zurück. Für ein Mock-Objekt mit einemspecgibt__class__stattdessen die Spec-Klasse zurück. Dies ermöglicht es Mock-Objekten,isinstance()-Tests für das Objekt, das sie ersetzen / vortäuschen, zu bestehen.>>> mock = Mock(spec=3) >>> isinstance(mock, int) True
__class__ist zuweisbar, dies ermöglicht einem Mock, eineisinstance()-Prüfung zu bestehen, ohne dass Sie ein Spec verwenden müssen.>>> mock = Mock() >>> mock.__class__ = dict >>> isinstance(mock, dict) True
- class unittest.mock.NonCallableMock(spec=None, wraps=None, name=None, spec_set=None, **kwargs)¶
Eine nicht aufrufbare Version von
Mock. Die Konstruktorparameter haben die gleiche Bedeutung wie beiMock, mit Ausnahme von return_value und side_effect, die bei einem nicht aufrufbaren Mock keine Bedeutung haben.
Mock-Objekte, die eine Klasse oder eine Instanz als spec oder spec_set verwenden, können isinstance()-Tests bestehen.
>>> mock = Mock(spec=SomeClass)
>>> isinstance(mock, SomeClass)
True
>>> mock = Mock(spec_set=SomeClass())
>>> isinstance(mock, SomeClass)
True
Die Mock-Klassen unterstützen das Mocken von magischen Methoden. Vollständige Details finden Sie unter magische Methoden.
Die Mock-Klassen und die patch()-Dekoratoren nehmen beliebige Schlüsselwortargumente zur Konfiguration entgegen. Für die patch()-Dekoratoren werden die Schlüsselwörter an den Konstruktor des erstellten Mocks übergeben. Die Schlüsselwortargumente dienen zur Konfiguration von Attributen des Mocks.
>>> m = MagicMock(attribute=3, other='fish')
>>> m.attribute
3
>>> m.other
'fish'
Der Rückgabewert und die Seiteneffekte von Kind-Mocks können auf dieselbe Weise gesetzt werden, über Punktnotation. Da Sie keine Punktnamen direkt in einem Aufruf verwenden können, müssen Sie ein Wörterbuch erstellen und es mit ** entpacken.
>>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> mock = Mock(some_attribute='eggs', **attrs)
>>> mock.some_attribute
'eggs'
>>> mock.method()
3
>>> mock.other()
Traceback (most recent call last):
...
KeyError
Ein aufrufbares Mock, das mit einem spec (oder spec_set) erstellt wurde, wird die Signatur des Spezifikationsobjekts beim Abgleich von Aufrufen mit dem Mock introspektieren. Daher kann es die Argumente des tatsächlichen Aufrufs abgleichen, unabhängig davon, ob sie positionell oder namentlich übergeben wurden.
>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, c=3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(1, 2, 3)
>>> mock.assert_called_with(a=1, b=2, c=3)
Dies gilt für assert_called_with(), assert_called_once_with(), assert_has_calls() und assert_any_call(). Beim Auto-Speccing gilt dies auch für Methodenaufrufe auf dem Mock-Objekt.
Geändert in Version 3.4: Signatur-Introspektion für gespecete und autospecete Mock-Objekte hinzugefügt.
- class unittest.mock.PropertyMock(*args, **kwargs)¶
Ein Mock, der als
propertyoder ein anderer Deskriptor auf einer Klasse verwendet werden soll.PropertyMockstellt die Methoden__get__()und__set__()bereit, damit Sie beim Abrufen einen Rückgabewert angeben können.Das Abrufen einer
PropertyMock-Instanz von einem Objekt ruft den Mock ohne Argumente auf. Das Setzen ruft den Mock mit dem zu setzenden Wert auf.>>> class Foo: ... @property ... def foo(self): ... return 'something' ... @foo.setter ... def foo(self, value): ... pass ... >>> with patch('__main__.Foo.foo', new_callable=PropertyMock) as mock_foo: ... mock_foo.return_value = 'mockity-mock' ... this_foo = Foo() ... print(this_foo.foo) ... this_foo.foo = 6 ... mockity-mock >>> mock_foo.mock_calls [call(), call(6)]
Aufgrund der Art und Weise, wie Mock-Attribute gespeichert werden, können Sie eine PropertyMock nicht direkt an ein Mock-Objekt anhängen. Stattdessen können Sie sie an das Mock-Typobjekt anhängen.
>>> m = MagicMock()
>>> p = PropertyMock(return_value=3)
>>> type(m).foo = p
>>> m.foo
3
>>> p.assert_called_once_with()
Vorsicht
Wenn ein AttributeError von PropertyMock ausgelöst wird, wird dies als fehlender Deskriptor interpretiert und __getattr__() wird für den Eltern-Mock aufgerufen.
>>> m = MagicMock()
>>> no_attribute = PropertyMock(side_effect=AttributeError)
>>> type(m).my_property = no_attribute
>>> m.my_property
<MagicMock name='mock.my_property' id='140165240345424'>
Details finden Sie unter __getattr__().
- class unittest.mock.AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)¶
Eine asynchrone Version von
MagicMock. DasAsyncMock-Objekt wird sich so verhalten, dass das Objekt als asynchrone Funktion erkannt wird und das Ergebnis eines Aufrufs awaitable ist.>>> mock = AsyncMock() >>> inspect.iscoroutinefunction(mock) True >>> inspect.isawaitable(mock()) True
Das Ergebnis von
mock()ist eine asynchrone Funktion, die nach dem Awaiten das Ergebnis vonside_effectoderreturn_valuehat.Wenn
side_effecteine Funktion ist, gibt die asynchrone Funktion das Ergebnis dieser Funktion zurück.Wenn
side_effecteine Ausnahme ist, löst die asynchrone Funktion die Ausnahme aus.Wenn
side_effectein Iterable ist, gibt die asynchrone Funktion den nächsten Wert des Iterables zurück. Wenn jedoch die Ergebnissequenz erschöpft ist, wirdStopAsyncIterationsofort ausgelöst.Wenn
side_effectnicht definiert ist, gibt die asynchrone Funktion den vonreturn_valuedefinierten Wert zurück, daher gibt die asynchrone Funktion standardmäßig ein neuesAsyncMock-Objekt zurück.
Das Setzen des spec eines
MockoderMagicMockauf eine asynchrone Funktion führt dazu, dass nach dem Aufruf ein Coroutine-Objekt zurückgegeben wird.>>> async def async_func(): pass ... >>> mock = MagicMock(async_func) >>> mock <MagicMock spec='function' id='...'> >>> mock() <coroutine object AsyncMockMixin._mock_call at ...>
Das Setzen des spec eines
Mock,MagicMockoderAsyncMockauf eine Klasse mit asynchronen und synchronen Funktionen erkennt automatisch die synchronen Funktionen und setzt sie alsMagicMock(wenn der Eltern-MockAsyncMockoderMagicMockist) oderMock(wenn der Eltern-MockMockist). Alle asynchronen Funktionen werdenAsyncMock.>>> class ExampleClass: ... def sync_foo(): ... pass ... async def async_foo(): ... pass ... >>> a_mock = AsyncMock(ExampleClass) >>> a_mock.sync_foo <MagicMock name='mock.sync_foo' id='...'> >>> a_mock.async_foo <AsyncMock name='mock.async_foo' id='...'> >>> mock = Mock(ExampleClass) >>> mock.sync_foo <Mock name='mock.sync_foo' id='...'> >>> mock.async_foo <AsyncMock name='mock.async_foo' id='...'>
Hinzugefügt in Version 3.8.
- assert_awaited()¶
Überprüft, ob der Mock mindestens einmal awaited wurde. Beachten Sie, dass dies getrennt davon ist, ob das Objekt aufgerufen wurde; das Schlüsselwort
awaitmuss verwendet werden.>>> mock = AsyncMock() >>> async def main(coroutine_mock): ... await coroutine_mock ... >>> coroutine_mock = mock() >>> mock.called True >>> mock.assert_awaited() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited. >>> asyncio.run(main(coroutine_mock)) >>> mock.assert_awaited()
- assert_awaited_once()¶
Überprüft, ob der Mock genau einmal awaited wurde.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.assert_awaited_once() >>> asyncio.run(main()) >>> mock.assert_awaited_once() Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_awaited_with(*args, **kwargs)¶
Überprüft, ob das letzte Await mit den angegebenen Argumenten erfolgte.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_with('foo', bar='bar') >>> mock.assert_awaited_with('other') Traceback (most recent call last): ... AssertionError: expected await not found. Expected: mock('other') Actual: mock('foo', bar='bar')
- assert_awaited_once_with(*args, **kwargs)¶
Überprüft, ob der Mock genau einmal und mit den angegebenen Argumenten awaited wurde.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') >>> asyncio.run(main('foo', bar='bar')) >>> mock.assert_awaited_once_with('foo', bar='bar') Traceback (most recent call last): ... AssertionError: Expected mock to have been awaited once. Awaited 2 times.
- assert_any_await(*args, **kwargs)¶
Überprüft, ob der Mock jemals mit den angegebenen Argumenten awaited wurde.
>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> asyncio.run(main('foo', bar='bar')) >>> asyncio.run(main('hello')) >>> mock.assert_any_await('foo', bar='bar') >>> mock.assert_any_await('other') Traceback (most recent call last): ... AssertionError: mock('other') await not found
- assert_has_awaits(calls, any_order=False)¶
Überprüft, ob der Mock mit den angegebenen Aufrufen awaited wurde. Die Liste
await_args_listwird auf Awaits überprüft.Wenn any_order falsch ist, müssen die Awaits sequenziell sein. Es kann zusätzliche Aufrufe vor oder nach den angegebenen Awaits geben.
Wenn any_order wahr ist, können die Awaits in beliebiger Reihenfolge erfolgen, sie müssen jedoch alle in
await_args_listerscheinen.>>> mock = AsyncMock() >>> async def main(*args, **kwargs): ... await mock(*args, **kwargs) ... >>> calls = [call("foo"), call("bar")] >>> mock.assert_has_awaits(calls) Traceback (most recent call last): ... AssertionError: Awaits not found. Expected: [call('foo'), call('bar')] Actual: [] >>> asyncio.run(main('foo')) >>> asyncio.run(main('bar')) >>> mock.assert_has_awaits(calls)
- assert_not_awaited()¶
Überprüft, ob der Mock niemals awaited wurde.
>>> mock = AsyncMock() >>> mock.assert_not_awaited()
- reset_mock(*args, **kwargs)¶
Siehe
Mock.reset_mock(). Setzt auchawait_countauf 0,await_argsauf None und löscht dieawait_args_list.
- await_count¶
Eine Ganzzahl, die zählt, wie oft das Mock-Objekt awaited wurde.
>>> mock = AsyncMock() >>> async def main(): ... await mock() ... >>> asyncio.run(main()) >>> mock.await_count 1 >>> asyncio.run(main()) >>> mock.await_count 2
- await_args¶
Dies ist entweder
None(wenn der Mock nicht awaited wurde) oder die Argumente, mit denen der Mock zuletzt awaited wurde. Funktioniert wieMock.call_args.>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args >>> asyncio.run(main('foo')) >>> mock.await_args call('foo') >>> asyncio.run(main('bar')) >>> mock.await_args call('bar')
- await_args_list¶
Dies ist eine Liste aller Awaits, die nacheinander an das Mock-Objekt gemacht wurden (die Länge der Liste entspricht also der Anzahl der Awaits). Bevor Awaits gemacht wurden, ist es eine leere Liste.
>>> mock = AsyncMock() >>> async def main(*args): ... await mock(*args) ... >>> mock.await_args_list [] >>> asyncio.run(main('foo')) >>> mock.await_args_list [call('foo')] >>> asyncio.run(main('bar')) >>> mock.await_args_list [call('foo'), call('bar')]
- class unittest.mock.ThreadingMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, *, timeout=UNSET, **kwargs)¶
Eine Version von
MagicMockfür Multithreading-Tests.ThreadingMockbietet zusätzliche Methoden, um auf den Aufruf eines Aufrufs zu warten, anstatt ihn sofort zu überprüfen.Das Standard-Timeout wird durch das Argument
timeoutoder, falls nicht gesetzt, durch das AttributThreadingMock.DEFAULT_TIMEOUTangegeben, das standardmäßig blockiert (None).Sie können das globale Standard-Timeout konfigurieren, indem Sie
ThreadingMock.DEFAULT_TIMEOUTsetzen.- wait_until_called(*, timeout=UNSET)¶
Wartet, bis der Mock aufgerufen wird.
Wenn ein Timeout bei der Erstellung des Mocks übergeben wurde oder ein Timeout-Argument an diese Funktion übergeben wird, löst die Funktion einen
AssertionErroraus, wenn der Aufruf nicht rechtzeitig erfolgt.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock) >>> thread.start() >>> mock.wait_until_called(timeout=1) >>> thread.join()
- wait_until_any_call_with(*args, **kwargs)¶
Wartet, bis der Mock mit den angegebenen Argumenten aufgerufen wird.
Wenn ein Timeout bei der Erstellung des Mocks übergeben wurde, löst die Funktion einen
AssertionErroraus, wenn der Aufruf nicht rechtzeitig erfolgt.>>> mock = ThreadingMock() >>> thread = threading.Thread(target=mock, args=("arg1", "arg2",), kwargs={"arg": "thing"}) >>> thread.start() >>> mock.wait_until_any_call_with("arg1", "arg2", arg="thing") >>> thread.join()
- DEFAULT_TIMEOUT¶
Globaler Standard-Timeout in Sekunden für die Erstellung von Instanzen von
ThreadingMock.
Hinzugefügt in Version 3.13.
Aufrufen¶
Mock-Objekte sind aufrufbar. Der Aufruf gibt den Wert zurück, der als return_value-Attribut gesetzt ist. Der Standardrückgabewert ist ein neues Mock-Objekt; es wird beim ersten Zugriff auf den Rückgabewert (entweder explizit oder durch Aufruf des Mocks) erstellt - aber es wird gespeichert und jedes Mal dasselbe zurückgegeben.
Aufrufe des Objekts werden in Attributen wie call_args und call_args_list aufgezeichnet.
Wenn side_effect gesetzt ist, wird es nach der Aufzeichnung des Aufrufs aufgerufen. Wenn side_effect eine Ausnahme auslöst, wird der Aufruf trotzdem aufgezeichnet.
Der einfachste Weg, einen Mock eine Ausnahme auslösen zu lassen, wenn er aufgerufen wird, besteht darin, side_effect eine Ausnahmeklasse oder -instanz sein zu lassen.
>>> m = MagicMock(side_effect=IndexError)
>>> m(1, 2, 3)
Traceback (most recent call last):
...
IndexError
>>> m.mock_calls
[call(1, 2, 3)]
>>> m.side_effect = KeyError('Bang!')
>>> m('two', 'three', 'four')
Traceback (most recent call last):
...
KeyError: 'Bang!'
>>> m.mock_calls
[call(1, 2, 3), call('two', 'three', 'four')]
Wenn side_effect eine Funktion ist, gibt das zurück, was diese Funktion zurückgibt, auch die Aufrufe des Mocks zurück. Die Funktion side_effect wird mit denselben Argumenten wie der Mock aufgerufen. Dies ermöglicht es Ihnen, den Rückgabewert des Aufrufs dynamisch basierend auf der Eingabe zu variieren.
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
Wenn der Mock immer noch den Standardrückgabewert (einen neuen Mock) oder einen gesetzten Rückgabewert zurückgeben soll, gibt es zwei Möglichkeiten. Entweder geben Sie return_value aus side_effect zurück, oder Sie geben DEFAULT zurück.
>>> m = MagicMock()
>>> def side_effect(*args, **kwargs):
... return m.return_value
...
>>> m.side_effect = side_effect
>>> m.return_value = 3
>>> m()
3
>>> def side_effect(*args, **kwargs):
... return DEFAULT
...
>>> m.side_effect = side_effect
>>> m()
3
Um einen side_effect zu entfernen und zum Standardverhalten zurückzukehren, setzen Sie side_effect auf None.
>>> m = MagicMock(return_value=6)
>>> def side_effect(*args, **kwargs):
... return 3
...
>>> m.side_effect = side_effect
>>> m()
3
>>> m.side_effect = None
>>> m()
6
Die side_effect kann auch jedes Iterable-Objekt sein. Wiederholte Aufrufe des Mocks geben Werte aus dem Iterable zurück (bis das Iterable erschöpft ist und eine StopIteration ausgelöst wird).
>>> m = MagicMock(side_effect=[1, 2, 3])
>>> m()
1
>>> m()
2
>>> m()
3
>>> m()
Traceback (most recent call last):
...
StopIteration
Wenn Mitglieder des Iterables Ausnahmen sind, werden diese statt zurückgegeben.
>>> iterable = (33, ValueError, 66)
>>> m = MagicMock(side_effect=iterable)
>>> m()
33
>>> m()
Traceback (most recent call last):
...
ValueError
>>> m()
66
Attribute löschen¶
Mock-Objekte erstellen Attribute bei Bedarf. Dies ermöglicht es ihnen, sich als Objekte beliebigen Typs auszugeben.
Sie möchten vielleicht, dass ein Mock-Objekt False für einen hasattr()-Aufruf zurückgibt oder einen AttributeError auslöst, wenn ein Attribut abgerufen wird. Dies können Sie erreichen, indem Sie ein Objekt als spec für einen Mock bereitstellen, aber das ist nicht immer praktisch.
Sie "blockieren" Attribute, indem Sie sie löschen. Sobald ein Attribut gelöscht wurde, löst der Zugriff darauf einen AttributeError aus.
>>> mock = MagicMock()
>>> hasattr(mock, 'm')
True
>>> del mock.m
>>> hasattr(mock, 'm')
False
>>> del mock.f
>>> mock.f
Traceback (most recent call last):
...
AttributeError: f
Mock-Namen und das Namensattribut¶
Da "name" ein Argument des Konstruktors von Mock ist, können Sie, wenn Sie möchten, dass Ihr Mock-Objekt ein "name"-Attribut hat, dieses nicht einfach bei der Erstellung übergeben. Es gibt zwei Alternativen. Eine Option ist die Verwendung von configure_mock().
>>> mock = MagicMock()
>>> mock.configure_mock(name='my_name')
>>> mock.name
'my_name'
Eine einfachere Option ist, das "name"-Attribut einfach nach der Mock-Erstellung zu setzen.
>>> mock = MagicMock()
>>> mock.name = "foo"
Mocks als Attribute anhängen¶
Wenn Sie einen Mock als Attribut eines anderen Mocks anhängen (oder als Rückgabewert), wird er zu einem "Kind" dieses Mocks. Aufrufe des Kindes werden in den Attributen method_calls und mock_calls des Elternteils aufgezeichnet. Dies ist nützlich, um Kind-Mocks zu konfigurieren und sie dann an den Elternteil anzuhängen oder Mocks an einen Elternteil anzuhängen, der alle Aufrufe an die Kinder aufzeichnet und es Ihnen ermöglicht, Aussagen über die Reihenfolge der Aufrufe zwischen Mocks zu treffen.
>>> parent = MagicMock()
>>> child1 = MagicMock(return_value=None)
>>> child2 = MagicMock(return_value=None)
>>> parent.child1 = child1
>>> parent.child2 = child2
>>> child1(1)
>>> child2(2)
>>> parent.mock_calls
[call.child1(1), call.child2(2)]
Die Ausnahme davon ist, wenn der Mock einen Namen hat. Dies ermöglicht es Ihnen, das "Parenting" zu verhindern, falls Sie es aus irgendeinem Grund nicht wünschen.
>>> mock = MagicMock()
>>> not_a_child = MagicMock(name='not-a-child')
>>> mock.attribute = not_a_child
>>> mock.attribute()
<MagicMock name='not-a-child()' id='...'>
>>> mock.mock_calls
[]
Mocks, die von patch() für Sie erstellt werden, erhalten automatisch Namen. Um Mocks mit Namen an einen Elternteil anzuhängen, verwenden Sie die Methode attach_mock().
>>> thing1 = object()
>>> thing2 = object()
>>> parent = MagicMock()
>>> with patch('__main__.thing1', return_value=None) as child1:
... with patch('__main__.thing2', return_value=None) as child2:
... parent.attach_mock(child1, 'child1')
... parent.attach_mock(child2, 'child2')
... child1('one')
... child2('two')
...
>>> parent.mock_calls
[call.child1('one'), call.child2('two')]
Die Patcher¶
Die Patch-Dekoratoren werden verwendet, um Objekte nur im Geltungsbereich der Funktion, die sie dekorieren, zu patchen. Sie kümmern sich automatisch um das Entpatchen, auch wenn Ausnahmen auftreten. Alle diese Funktionen können auch in with-Anweisungen oder als Klassen-Dekoratoren verwendet werden.
patch¶
Hinweis
Der Schlüssel ist, das Patchen im richtigen Namensraum durchzuführen. Siehe den Abschnitt Wo patchen.
- unittest.mock.patch(target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
patch()fungiert als Funktionsdecorator, Klassendecorator oder Kontextmanager. Innerhalb des Funktionskörpers oder der with-Anweisung wird das target durch ein new-Objekt ersetzt (gepatcht). Beim Verlassen der Funktion/with-Anweisung wird das Patching rückgängig gemacht.Wenn new weggelassen wird, wird das Ziel durch einen
AsyncMockersetzt, wenn das gepatchte Objekt eine asynchrone Funktion ist, andernfalls durch einenMagicMock. Wennpatch()als Decorator verwendet wird und new weggelassen wird, wird der erstellte Mock als zusätzliches Argument an die dekorierte Funktion übergeben. Wennpatch()als Kontextmanager verwendet wird, wird der erstellte Mock vom Kontextmanager zurückgegeben.target sollte ein String im Format
'paket.modul.Klassenname'sein. Das target wird importiert und das angegebene Objekt wird durch das new-Objekt ersetzt, daher muss das target aus der Umgebung importierbar sein, aus der Siepatch()aufrufen. Das Ziel wird beim Ausführen der dekorierten Funktion importiert, nicht zum Dekorationszeitpunkt.Die Schlüsselwörter spec und spec_set werden an
MagicMockübergeben, wenn patch eines für Sie erstellt.Zusätzlich können Sie
spec=Trueoderspec_set=Trueübergeben, was dazu führt, dass patch das zu mockende Objekt als spec/spec_set-Objekt übergibt.new_callable ermöglicht es Ihnen, eine andere Klasse oder ein aufrufbare Objekt anzugeben, das aufgerufen wird, um das new-Objekt zu erstellen. Standardmäßig wird
AsyncMockfür asynchrone Funktionen undMagicMockfür die übrigen verwendet.Eine leistungsfähigere Form von spec ist autospec. Wenn Sie
autospec=Truesetzen, wird der Mock mit einem Spec des zu ersetzenden Objekts erstellt. Alle Attribute des Mocks haben ebenfalls den Spec des entsprechenden Attributs des zu ersetzenden Objekts. Methoden und Funktionen, die gemockt werden, werden auf ihre Argumente überprüft und lösen eineTypeErroraus, wenn sie mit der falschen Signatur aufgerufen werden. Für Mocks, die eine Klasse ersetzen, hat ihr Rückgabewert (die „Instanz“) denselben Spec wie die Klasse. Siehe die Funktioncreate_autospec()und Autospeccing.Anstatt
autospec=Truekönnen Sieautospec=some_objectübergeben, um ein beliebiges Objekt als Spec zu verwenden, anstatt des zu ersetzenden.Standardmäßig schlägt
patch()fehl, wenn versucht wird, Attribute zu ersetzen, die nicht existieren. Wenn Siecreate=Trueübergeben und das Attribut nicht existiert, erstellt patch das Attribut für Sie, wenn die gepatchte Funktion aufgerufen wird, und löscht es wieder, nachdem die gepatchte Funktion beendet wurde. Dies ist nützlich, um Tests gegen Attribute zu schreiben, die Ihr Produktionscode zur Laufzeit erstellt. Es ist standardmäßig deaktiviert, da es gefährlich sein kann. Mit aktivierter Option können Sie funktionierende Tests gegen APIs schreiben, die tatsächlich nicht existieren!Hinweis
Geändert in Version 3.5: Wenn Sie Built-ins in einem Modul patchen, müssen Sie
create=Truenicht übergeben, es wird standardmäßig hinzugefügt.Patch kann als
TestCase-Klassen-Decorator verwendet werden. Dies geschieht durch Dekorieren jeder Testmethode in der Klasse. Dies reduziert den Boilerplate-Code, wenn Ihre Testmethoden ein gemeinsames Patching-Set haben.patch()findet Tests, indem es nach Methodennamen sucht, die mitpatch.TEST_PREFIXbeginnen. Standardmäßig ist dies'test', was der Art und Weise entspricht, wieunittestTests findet. Sie können ein alternatives Präfix angeben, indem Siepatch.TEST_PREFIXsetzen.Patch kann als Kontextmanager mit der with-Anweisung verwendet werden. Hier gilt das Patchen für den eingerückten Block nach der with-Anweisung. Wenn Sie „as“ verwenden, wird das gepatchte Objekt an den Namen nach „as“ gebunden; dies ist sehr nützlich, wenn
patch()ein Mock-Objekt für Sie erstellt.patch()akzeptiert beliebige Schlüsselwortargumente. Diese werden anAsyncMockübergeben, wenn das gepatchte Objekt asynchron ist, andernfalls anMagicMockoder an new_callable, falls angegeben.patch.dict(...),patch.multiple(...)undpatch.object(...)sind für alternative Anwendungsfälle verfügbar.
patch() als Funktionsdecorator, der den Mock für Sie erstellt und ihn an die dekorierte Funktion übergibt
>>> @patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True
Das Patchen einer Klasse ersetzt die Klasse durch eine MagicMock-Instanz. Wenn die Klasse im zu testenden Code instanziiert wird, wird die return_value des Mocks verwendet.
Wenn die Klasse mehrmals instanziiert wird, können Sie side_effect verwenden, um jedes Mal einen neuen Mock zurückzugeben. Alternativ können Sie return_value beliebig festlegen.
Um Rückgabewerte für Methoden von Instanzen der gepatchten Klasse zu konfigurieren, müssen Sie dies auf dem return_value tun. Zum Beispiel
>>> class Class:
... def method(self):
... pass
...
>>> with patch('__main__.Class') as MockClass:
... instance = MockClass.return_value
... instance.method.return_value = 'foo'
... assert Class() is instance
... assert Class().method() == 'foo'
...
Wenn Sie spec oder spec_set verwenden und patch() eine Klasse ersetzt, hat der Rückgabewert des erstellten Mocks denselben Spec.
>>> Original = Class
>>> patcher = patch('__main__.Class', spec=True)
>>> MockClass = patcher.start()
>>> instance = MockClass()
>>> assert isinstance(instance, Original)
>>> patcher.stop()
Das Argument new_callable ist nützlich, wenn Sie eine alternative Klasse zu MagicMock für den erstellten Mock verwenden möchten. Zum Beispiel, wenn Sie einen NonCallableMock verwenden möchten
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
Ein weiterer Anwendungsfall könnte sein, ein Objekt durch eine io.StringIO-Instanz zu ersetzen
>>> from io import StringIO
>>> def foo():
... print('Something')
...
>>> @patch('sys.stdout', new_callable=StringIO)
... def test(mock_stdout):
... foo()
... assert mock_stdout.getvalue() == 'Something\n'
...
>>> test()
Wenn patch() einen Mock für Sie erstellt, ist oft das erste, was Sie tun müssen, die Konfiguration des Mocks. Einige dieser Konfigurationen können im Aufruf von patch vorgenommen werden. Beliebige Schlüsselwörter, die Sie an den Aufruf übergeben, werden verwendet, um Attribute auf dem erstellten Mock zu setzen
>>> patcher = patch('__main__.thing', first='one', second='two')
>>> mock_thing = patcher.start()
>>> mock_thing.first
'one'
>>> mock_thing.second
'two'
Neben Attributen auf dem erstellten Mock können auch Attribute von Kind-Mocks, wie return_value und side_effect, konfiguriert werden. Diese sind syntaktisch nicht direkt als Schlüsselwortargumente gültig, aber ein Dictionary mit diesen als Schlüssel kann immer noch durch ** in einen patch()-Aufruf erweitert werden
>>> config = {'method.return_value': 3, 'other.side_effect': KeyError}
>>> patcher = patch('__main__.thing', **config)
>>> mock_thing = patcher.start()
>>> mock_thing.method()
3
>>> mock_thing.other()
Traceback (most recent call last):
...
KeyError
Standardmäßig schlägt der Versuch, eine Funktion in einem Modul (oder eine Methode oder ein Attribut in einer Klasse) zu patchen, die nicht existiert, mit einem AttributeError fehl
>>> @patch('sys.non_existing_attribute', 42)
... def test():
... assert sys.non_existing_attribute == 42
...
>>> test()
Traceback (most recent call last):
...
AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'
aber das Hinzufügen von create=True im Aufruf von patch() lässt das vorherige Beispiel wie erwartet funktionieren
>>> @patch('sys.non_existing_attribute', 42, create=True)
... def test(mock_stdout):
... assert sys.non_existing_attribute == 42
...
>>> test()
patch.object¶
- patch.object(target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
Patcht das benannte Mitglied (attribute) eines Objekts (target) mit einem Mock-Objekt.
patch.object()kann als Decorator, Klassen-Decorator oder Kontextmanager verwendet werden. Die Argumente new, spec, create, spec_set, autospec und new_callable haben dieselbe Bedeutung wie beipatch(). Wiepatch()akzeptiertpatch.object()beliebige Schlüsselwortargumente zur Konfiguration des von ihm erstellten Mock-Objekts.Wenn
patch.object()als Klassen-Decorator verwendet wird, beachtet espatch.TEST_PREFIXbei der Auswahl der zu umschließenden Methoden.
Sie können patch.object() entweder mit drei oder zwei Argumenten aufrufen. Die Drei-Argumente-Form nimmt das zu patchende Objekt, den Attributnamen und das Objekt, das das Attribut ersetzen soll.
Bei einem Aufruf mit der Zwei-Argumente-Form lassen Sie das Ersatzobjekt weg, und ein Mock wird für Sie erstellt und als zusätzliches Argument an die dekorierte Funktion übergeben
>>> @patch.object(SomeClass, 'class_method')
... def test(mock_method):
... SomeClass.class_method(3)
... mock_method.assert_called_with(3)
...
>>> test()
spec, create und die anderen Argumente von patch.object() haben dieselbe Bedeutung wie bei patch().
patch.dict¶
- patch.dict(in_dict, values=(), clear=False, **kwargs)¶
Patchen eines Dictionarys oder eines dictionary-ähnlichen Objekts und Wiederherstellen des Dictionarys in seinen ursprünglichen Zustand nach dem Test, wobei das wiederhergestellte Dictionary eine Kopie des Dictionarys ist, wie es vor dem Test war.
in_dict kann ein Dictionary oder ein Mapping-ähnlicher Container sein. Wenn es sich um ein Mapping handelt, muss es mindestens das Holen, Setzen und Löschen von Elementen sowie das Iterieren über Schlüssel unterstützen.
in_dict kann auch ein String sein, der den Namen des Dictionarys angibt, der dann durch Importieren abgerufen wird.
values kann ein Dictionary von Werten sein, die im Dictionary gesetzt werden. values kann auch ein iterierbares Objekt von
(key, value)-Paaren sein.Wenn clear wahr ist, wird das Dictionary gelöscht, bevor die neuen Werte gesetzt werden.
patch.dict()kann auch mit beliebigen Schlüsselwortargumenten aufgerufen werden, um Werte im Dictionary zu setzen.Geändert in Version 3.8:
patch.dict()gibt nun das gepatchte Dictionary zurück, wenn es als Kontextmanager verwendet wird.
patch.dict() kann als Kontextmanager, Decorator oder Klassen-Decorator verwendet werden
>>> foo = {}
>>> @patch.dict(foo, {'newkey': 'newvalue'})
... def test():
... assert foo == {'newkey': 'newvalue'}
...
>>> test()
>>> assert foo == {}
Wenn patch.dict() als Klassen-Decorator verwendet wird, beachtet es patch.TEST_PREFIX (standardmäßig 'test') bei der Auswahl der zu umschließenden Methoden
>>> import os
>>> import unittest
>>> from unittest.mock import patch
>>> @patch.dict('os.environ', {'newkey': 'newvalue'})
... class TestSample(unittest.TestCase):
... def test_sample(self):
... self.assertEqual(os.environ['newkey'], 'newvalue')
Wenn Sie ein anderes Präfix für Ihren Test verwenden möchten, können Sie die Patcher mit dem anderen Präfix informieren, indem Sie patch.TEST_PREFIX setzen. Weitere Details zur Änderung des Wertes finden Sie unter TEST_PREFIX.
patch.dict() kann verwendet werden, um Elemente zu einem Dictionary hinzuzufügen oder einfach einen Test ein Dictionary ändern zu lassen und sicherzustellen, dass das Dictionary wiederhergestellt wird, wenn der Test endet.
>>> foo = {}
>>> with patch.dict(foo, {'newkey': 'newvalue'}) as patched_foo:
... assert foo == {'newkey': 'newvalue'}
... assert patched_foo == {'newkey': 'newvalue'}
... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)
... patched_foo['spam'] = 'eggs'
...
>>> assert foo == {}
>>> assert patched_foo == {}
>>> import os
>>> with patch.dict('os.environ', {'newkey': 'newvalue'}):
... print(os.environ['newkey'])
...
newvalue
>>> assert 'newkey' not in os.environ
Schlüsselwörter können im Aufruf von patch.dict() verwendet werden, um Werte im Dictionary zu setzen
>>> mymodule = MagicMock()
>>> mymodule.function.return_value = 'fish'
>>> with patch.dict('sys.modules', mymodule=mymodule):
... import mymodule
... mymodule.function('some', 'args')
...
'fish'
patch.dict() kann mit dictionary-ähnlichen Objekten verwendet werden, die nicht tatsächlich Dictionaries sind. Mindestens müssen sie Elementabruf, -setzung, -löschung und entweder Iteration oder Mitgliedschaftstest unterstützen. Dies entspricht den magischen Methoden __getitem__(), __setitem__(), __delitem__() und entweder __iter__() oder __contains__().
>>> class Container:
... def __init__(self):
... self.values = {}
... def __getitem__(self, name):
... return self.values[name]
... def __setitem__(self, name, value):
... self.values[name] = value
... def __delitem__(self, name):
... del self.values[name]
... def __iter__(self):
... return iter(self.values)
...
>>> thing = Container()
>>> thing['one'] = 1
>>> with patch.dict(thing, one=2, two=3):
... assert thing['one'] == 2
... assert thing['two'] == 3
...
>>> assert thing['one'] == 1
>>> assert list(thing) == ['one']
patch.multiple¶
- patch.multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs)¶
Führt mehrere Patches in einem einzigen Aufruf durch. Nimmt das zu patchende Objekt (entweder als Objekt oder als String zum Abrufen des Objekts durch Importieren) und Schlüsselwortargumente für die Patches entgegen
with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ...
Verwenden Sie
DEFAULTals Wert, wenn Sie möchten, dasspatch.multiple()Mocks für Sie erstellt. In diesem Fall werden die erstellten Mocks per Schlüsselwort an eine dekorierte Funktion übergeben, und ein Dictionary wird zurückgegeben, wennpatch.multiple()als Kontextmanager verwendet wird.patch.multiple()kann als Decorator, Klassen-Decorator oder Kontextmanager verwendet werden. Die Argumente spec, spec_set, create, autospec und new_callable haben dieselbe Bedeutung wie beipatch(). Diese Argumente werden auf alle vonpatch.multiple()durchgeführten Patches angewendet.Wenn
patch.multiple()als Klassen-Decorator verwendet wird, beachtet espatch.TEST_PREFIXbei der Auswahl der zu umschließenden Methoden.
Wenn Sie möchten, dass patch.multiple() Mocks für Sie erstellt, können Sie DEFAULT als Wert verwenden. Wenn Sie patch.multiple() als Decorator verwenden, werden die erstellten Mocks per Schlüsselwort an die dekorierte Funktion übergeben.
>>> thing = object()
>>> other = object()
>>> @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(thing, other):
... assert isinstance(thing, MagicMock)
... assert isinstance(other, MagicMock)
...
>>> test_function()
patch.multiple() kann mit anderen patch-Decorators verschachtelt werden, aber Schlüsselwortargumente müssen nach den Standardargumenten, die von patch() erstellt wurden, übergeben werden
>>> @patch('sys.exit')
... @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
... def test_function(mock_exit, other, thing):
... assert 'other' in repr(other)
... assert 'thing' in repr(thing)
... assert 'exit' in repr(mock_exit)
...
>>> test_function()
Wenn patch.multiple() als Kontextmanager verwendet wird, ist der vom Kontextmanager zurückgegebene Wert ein Dictionary, in dem die erstellten Mocks nach Namen indiziert sind
>>> with patch.multiple('__main__', thing=DEFAULT, other=DEFAULT) as values:
... assert 'other' in repr(values['other'])
... assert 'thing' in repr(values['thing'])
... assert values['thing'] is thing
... assert values['other'] is other
...
Patch-Methoden: start und stop¶
Alle Patcher haben die Methoden start() und stop(). Diese erleichtern das Patchen in setUp-Methoden oder wenn Sie mehrere Patches ohne verschachtelte Decorators oder with-Anweisungen durchführen möchten.
Um sie zu verwenden, rufen Sie patch(), patch.object() oder patch.dict() wie gewohnt auf und behalten Sie eine Referenz auf das zurückgegebene patcher-Objekt. Sie können dann start() aufrufen, um das Patch zu aktivieren, und stop(), um es rückgängig zu machen.
Wenn Sie patch() verwenden, um einen Mock für Sie zu erstellen, wird dieser vom Aufruf von patcher.start zurückgegeben.
>>> patcher = patch('package.module.ClassName')
>>> from package import module
>>> original = module.ClassName
>>> new_mock = patcher.start()
>>> assert module.ClassName is not original
>>> assert module.ClassName is new_mock
>>> patcher.stop()
>>> assert module.ClassName is original
>>> assert module.ClassName is not new_mock
Ein typischer Anwendungsfall hierfür könnte das Durchführen mehrerer Patches in der setUp-Methode eines TestCase sein
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... self.patcher1 = patch('package.module.Class1')
... self.patcher2 = patch('package.module.Class2')
... self.MockClass1 = self.patcher1.start()
... self.MockClass2 = self.patcher2.start()
...
... def tearDown(self):
... self.patcher1.stop()
... self.patcher2.stop()
...
... def test_something(self):
... assert package.module.Class1 is self.MockClass1
... assert package.module.Class2 is self.MockClass2
...
>>> MyTest('test_something').run()
Vorsicht
Wenn Sie diese Technik verwenden, müssen Sie sicherstellen, dass das Patchen „rückgängig gemacht“ wird, indem Sie stop aufrufen. Dies kann kniffliger sein, als Sie denken, denn wenn eine Ausnahme in der setUp ausgelöst wird, wird tearDown nicht aufgerufen. unittest.TestCase.addCleanup() erleichtert dies
>>> class MyTest(unittest.TestCase):
... def setUp(self):
... patcher = patch('package.module.Class')
... self.MockClass = patcher.start()
... self.addCleanup(patcher.stop)
...
... def test_something(self):
... assert package.module.Class is self.MockClass
...
Als zusätzlicher Bonus müssen Sie keine Referenz mehr auf das patcher-Objekt halten.
Es ist auch möglich, alle gestarteten Patches zu stoppen, indem patch.stopall() verwendet wird.
- patch.stopall()¶
Stoppt alle aktiven Patches. Stoppt nur Patches, die mit
startgestartet wurden.
Patch-Builtins¶
Sie können beliebige Built-ins innerhalb eines Moduls patchen. Das folgende Beispiel patchet die Builtin-Funktion ord()
>>> @patch('__main__.ord')
... def test(mock_ord):
... mock_ord.return_value = 101
... print(ord('c'))
...
>>> test()
101
TEST_PREFIX¶
Alle Patcher können als Klassen-Decorators verwendet werden. Wenn sie auf diese Weise verwendet werden, umschließen sie jede Testmethode der Klasse. Die Patcher erkennen Methoden, die mit 'test' beginnen, als Testmethoden. Dies ist dieselbe Methode, mit der der unittest.TestLoader standardmäßig Testmethoden findet.
Es ist möglich, dass Sie ein anderes Präfix für Ihre Tests verwenden möchten. Sie können die Patcher über das andere Präfix informieren, indem Sie patch.TEST_PREFIX setzen
>>> patch.TEST_PREFIX = 'foo'
>>> value = 3
>>>
>>> @patch('__main__.value', 'not three')
... class Thing:
... def foo_one(self):
... print(value)
... def foo_two(self):
... print(value)
...
>>>
>>> Thing().foo_one()
not three
>>> Thing().foo_two()
not three
>>> value
3
Verschachtelte Patch-Decorators¶
Wenn Sie mehrere Patches durchführen möchten, können Sie die Decorators einfach stapeln.
Sie können mehrere Patch-Decorators mit diesem Muster stapeln
>>> @patch.object(SomeClass, 'class_method')
... @patch.object(SomeClass, 'static_method')
... def test(mock1, mock2):
... assert SomeClass.static_method is mock1
... assert SomeClass.class_method is mock2
... SomeClass.static_method('foo')
... SomeClass.class_method('bar')
... return mock1, mock2
...
>>> mock1, mock2 = test()
>>> mock1.assert_called_once_with('foo')
>>> mock2.assert_called_once_with('bar')
Beachten Sie, dass die Decorators von unten nach oben angewendet werden. Dies ist die Standardweise, wie Python Decorators anwendet. Die Reihenfolge der erstellten Mocks, die an Ihre Testfunktion übergeben werden, entspricht dieser Reihenfolge.
Wo patchen¶
patch() funktioniert, indem es das Objekt, auf das ein Name zeigt, (vorübergehend) durch ein anderes ändert. Es kann viele Namen geben, die auf dasselbe Objekt zeigen. Damit das Patchen funktioniert, müssen Sie sicherstellen, dass Sie den Namen patchen, der vom zu testenden System verwendet wird.
Das Grundprinzip ist, dass Sie dort patchen, wo ein Objekt nachgeschlagen wird, was nicht unbedingt derselbe Ort ist, an dem es definiert ist. Einige Beispiele verdeutlichen dies.
Stellen Sie sich vor, wir haben ein Projekt, das wir mit der folgenden Struktur testen möchten
a.py
-> Defines SomeClass
b.py
-> from a import SomeClass
-> some_function instantiates SomeClass
Nun möchten wir some_function testen, aber wir möchten SomeClass mit patch() mocken. Das Problem ist, dass wenn wir Modul b importieren, was wir tun müssen, wenn es SomeClass aus Modul a importiert. Wenn wir patch() verwenden, um a.SomeClass zu mocken, hat dies keine Auswirkung auf unseren Test; Modul b hat bereits eine Referenz auf die echte SomeClass und es sieht so aus, als ob unser Patchen keine Wirkung hatte.
Der Schlüssel ist, SomeClass dort zu patchen, wo sie verwendet wird (oder wo sie nachgeschlagen wird). In diesem Fall wird some_function tatsächlich SomeClass in Modul b nachschlagen, wo wir sie importiert haben. Das Patchen sollte so aussehen
@patch('b.SomeClass')
Betrachten Sie jedoch das alternative Szenario, in dem Modul b anstelle von from a import SomeClass import a macht und some_function a.SomeClass verwendet. Beide Importformen sind üblich. In diesem Fall wird die zu patchende Klasse im Modul nachgeschlagen, und daher müssen wir a.SomeClass stattdessen patchen
@patch('a.SomeClass')
Patchen von Deskriptoren und Proxy-Objekten¶
Sowohl patch als auch patch.object patchen und stellen Deskriptoren korrekt wieder her: Klassenmethoden, statische Methoden und Eigenschaften. Sie sollten diese auf der Klasse und nicht auf einer Instanz patchen. Sie funktionieren auch mit einigen Objekten, die Attributzugriffe proxyen, wie dem Django-Settings-Objekt.
MagicMock und Unterstützung für magische Methoden¶
Mocking von magischen Methoden¶
Mock unterstützt das Mocking der Python-Protokollmethoden, auch bekannt als „magische Methoden“. Dies ermöglicht es Mock-Objekten, Container oder andere Objekte zu ersetzen, die Python-Protokolle implementieren.
Da magische Methoden anders abgefragt werden als normale Methoden [2], wurde diese Unterstützung speziell implementiert. Das bedeutet, dass nur bestimmte magische Methoden unterstützt werden. Die Liste der unterstützten Methoden umfasst *fast* alle. Wenn welche fehlen, die Sie benötigen, lassen Sie es uns bitte wissen.
Sie mocken magische Methoden, indem Sie die gewünschte Methode auf eine Funktion oder eine Mock-Instanz setzen. Wenn Sie eine Funktion verwenden, *muss* diese self als erstes Argument entgegennehmen [3].
>>> def __str__(self):
... return 'fooble'
...
>>> mock = Mock()
>>> mock.__str__ = __str__
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__str__ = Mock()
>>> mock.__str__.return_value = 'fooble'
>>> str(mock)
'fooble'
>>> mock = Mock()
>>> mock.__iter__ = Mock(return_value=iter([]))
>>> list(mock)
[]
Ein Anwendungsfall dafür ist das Mocking von Objekten, die als Kontextmanager in einer with-Anweisung verwendet werden.
>>> mock = Mock()
>>> mock.__enter__ = Mock(return_value='foo')
>>> mock.__exit__ = Mock(return_value=False)
>>> with mock as m:
... assert m == 'foo'
...
>>> mock.__enter__.assert_called_with()
>>> mock.__exit__.assert_called_with(None, None, None)
Aufrufe magischer Methoden erscheinen nicht in method_calls, werden aber in mock_calls aufgezeichnet.
Hinweis
Wenn Sie das Schlüsselwortargument spec verwenden, um einen Mock zu erstellen, löst der Versuch, eine magische Methode festzulegen, die nicht im Spec enthalten ist, einen AttributeError aus.
Die vollständige Liste der unterstützten magischen Methoden ist:
__hash__,__sizeof__,__repr__und__str____dir__,__format__und__subclasses____round__,__floor__,__trunc__und__ceil__Vergleiche:
__lt__,__gt__,__le__,__ge__,__eq__und__ne__Container-Methoden:
__getitem__,__setitem__,__delitem__,__contains__,__len__,__iter__,__reversed__und__missing__Kontextmanager:
__enter__,__exit__,__aenter__und__aexit__Unäre numerische Methoden:
__neg__,__pos__und__invert__Die numerischen Methoden (einschließlich rechtsseitiger und In-Place-Varianten):
__add__,__sub__,__mul__,__matmul__,__truediv__,__floordiv__,__mod__,__divmod__,__lshift__,__rshift__,__and__,__xor__,__or__und__pow__Numerische Konvertierungsmethoden:
__complex__,__int__,__float__und__index__Deskriptormethoden:
__get__,__set__und__delete__Pickling:
__reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__und__setstate__Dateisystempfadrepräsentation:
__fspath__Asynchrone Iterationsmethoden:
__aiter__und__anext__
Geändert in Version 3.8: Unterstützung für os.PathLike.__fspath__() hinzugefügt.
Geändert in Version 3.8: Unterstützung für __aenter__, __aexit__, __aiter__ und __anext__ hinzugefügt.
Die folgenden Methoden existieren, werden aber *nicht* unterstützt, da sie entweder von Mock verwendet werden, nicht dynamisch festgelegt werden können oder Probleme verursachen können.
__getattr__,__setattr__,__init__und__new____prepare__,__instancecheck__,__subclasscheck__,__del__
Magic Mock¶
Es gibt zwei MagicMock-Varianten: MagicMock und NonCallableMagicMock.
- class unittest.mock.MagicMock(*args, **kw)¶
MagicMockist eine Unterklasse vonMockmit Standardimplementierungen der meisten magischen Methoden. Sie könnenMagicMockverwenden, ohne die magischen Methoden selbst konfigurieren zu müssen.Die Konstruktorparameter haben die gleiche Bedeutung wie bei
Mock.Wenn Sie die Argumente spec oder spec_set verwenden, werden *nur* magische Methoden erstellt, die im Spec vorhanden sind.
- class unittest.mock.NonCallableMagicMock(*args, **kw)¶
Eine nicht aufrufbare Version von
MagicMock.Die Konstruktorparameter haben die gleiche Bedeutung wie bei
MagicMock, mit Ausnahme von return_value und side_effect, die bei einem nicht aufrufbaren Mock keine Bedeutung haben.
Die magischen Methoden werden mit MagicMock-Objekten eingerichtet, sodass Sie sie konfigurieren und wie gewohnt verwenden können.
>>> mock = MagicMock()
>>> mock[3] = 'fish'
>>> mock.__setitem__.assert_called_with(3, 'fish')
>>> mock.__getitem__.return_value = 'result'
>>> mock[2]
'result'
Standardmäßig müssen viele der Protokollmethoden Objekte eines bestimmten Typs zurückgeben. Diese Methoden sind mit einem Standardrückgabewert vorkonfiguriert, sodass sie ohne Ihr Zutun verwendet werden können, wenn Sie am Rückgabewert nicht interessiert sind. Sie können den Rückgabewert immer noch manuell *festlegen*, wenn Sie den Standard ändern möchten.
Methoden und ihre Standardwerte
__lt__:NotImplemented__gt__:NotImplemented__le__:NotImplemented__ge__:NotImplemented__int__:1__contains__:False__len__:0__iter__:iter([])__exit__:False__aexit__:False__complex__:1j__float__:1.0__bool__:True__index__:1__hash__: Standard-Hash für den Mock__str__: Standard-Str für den Mock__sizeof__: Standard-Sizeof für den Mock
Zum Beispiel
>>> mock = MagicMock()
>>> int(mock)
1
>>> len(mock)
0
>>> list(mock)
[]
>>> object() in mock
False
Die beiden Gleichheitsmethoden, __eq__() und __ne__(), sind besonders. Sie führen den Standardvergleich auf Identitätsebene durch und verwenden das Attribut side_effect, es sei denn, Sie ändern ihren Rückgabewert, um etwas anderes zurückzugeben.
>>> MagicMock() == 3
False
>>> MagicMock() != 3
True
>>> mock = MagicMock()
>>> mock.__eq__.return_value = True
>>> mock == 3
True
Der Rückgabewert von MagicMock.__iter__() kann jedes iterierbare Objekt sein und muss kein Iterator sein.
>>> mock = MagicMock()
>>> mock.__iter__.return_value = ['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
['a', 'b', 'c']
Wenn der Rückgabewert *ein* Iterator ist, wird er bei einmaligem Iterieren verbraucht und nachfolgende Iterationen ergeben eine leere Liste.
>>> mock.__iter__.return_value = iter(['a', 'b', 'c'])
>>> list(mock)
['a', 'b', 'c']
>>> list(mock)
[]
MagicMock hat alle unterstützten magischen Methoden konfiguriert, außer einigen obskuren und veralteten. Sie können diese trotzdem einrichten, wenn Sie möchten.
Magische Methoden, die unterstützt, aber in MagicMock nicht standardmäßig eingerichtet sind, sind:
__subclasses____dir____format____get__,__set__und__delete____reversed__und__missing____reduce__,__reduce_ex__,__getinitargs__,__getnewargs__,__getstate__und__setstate____getformat__
Magische Methoden *sollten* auf der Klasse und nicht auf der Instanz abgefragt werden. Unterschiedliche Python-Versionen sind inkonsistent in der Anwendung dieser Regel. Die unterstützten Protokollmethoden sollten mit allen unterstützten Python-Versionen funktionieren.
Die Funktion ist im Wesentlichen an die Klasse gebunden, aber jede Mock-Instanz wird von den anderen isoliert gehalten.
Helfer¶
sentinel¶
- unittest.mock.sentinel¶
Das
sentinel-Objekt bietet eine bequeme Möglichkeit, eindeutige Objekte für Ihre Tests bereitzustellen.Attribute werden bei Bedarf erstellt, wenn Sie sie nach Namen aufrufen. Der Zugriff auf dasselbe Attribut gibt immer dasselbe Objekt zurück. Die zurückgegebenen Objekte haben eine sinnvolle Darstellung, sodass Fehlermeldungen im Test lesbar sind.
Manchmal müssen Sie beim Testen überprüfen, ob ein bestimmtes Objekt als Argument an eine andere Methode übergeben oder zurückgegeben wird. Es kann üblich sein, benannte Sentinel-Objekte zu erstellen, um dies zu testen. sentinel bietet eine bequeme Möglichkeit, Objekte wie diese zu erstellen und ihre Identität zu testen.
In diesem Beispiel patchen wir method, um sentinel.some_object zurückzugeben.
>>> real = ProductionClass()
>>> real.method = Mock(name="method")
>>> real.method.return_value = sentinel.some_object
>>> result = real.method()
>>> assert result is sentinel.some_object
>>> result
sentinel.some_object
DEFAULT¶
- unittest.mock.DEFAULT¶
Das
DEFAULT-Objekt ist ein vordefiniertes Sentinel (tatsächlichsentinel.DEFAULT). Es kann vonside_effect-Funktionen verwendet werden, um anzuzeigen, dass der normale Rückgabewert verwendet werden soll.
call¶
- unittest.mock.call(*args, **kwargs)¶
call()ist ein Hilfsobjekt zur Vereinfachung von Assertionen, zum Vergleichen mitcall_args,call_args_list,mock_callsundmethod_calls.call()kann auch mitassert_has_calls()verwendet werden.>>> m = MagicMock(return_value=None) >>> m(1, 2, a='foo', b='bar') >>> m() >>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()] True
- call.call_list()¶
Für ein Aufrufobjekt, das mehrere Aufrufe repräsentiert, gibt
call_list()eine Liste aller Zwischenaufrufe sowie des letzten Aufrufs zurück.
call_list ist besonders nützlich für Assertionen über „verkettete Aufrufe“. Ein verketteter Aufruf sind mehrere Aufrufe in einer einzigen Codezeile. Dies führt zu mehreren Einträgen in mock_calls auf einem Mock. Die manuelle Konstruktion der Aufrufsequenz kann mühsam sein.
call_list() kann die Aufrufsequenz aus demselben verketteten Aufruf erstellen.
>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
call().method(arg='foo'),
call().method().other('bar'),
call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True
Ein call-Objekt ist entweder ein Tupel aus (Positionsargumente, Schlüsselwortargumente) oder (Name, Positionsargumente, Schlüsselwortargumente), je nachdem, wie es erstellt wurde. Wenn Sie sie selbst erstellen, ist das nicht besonders interessant, aber die call-Objekte, die sich in den Attributen Mock.call_args, Mock.call_args_list und Mock.mock_calls befinden, können introspektiert werden, um an die einzelnen darin enthaltenen Argumente zu gelangen.
Die call-Objekte in Mock.call_args und Mock.call_args_list sind Zweiertupel aus (Positionsargumente, Schlüsselwortargumente), während die call-Objekte in Mock.mock_calls sowie die, die Sie selbst erstellen, Dreiertupel aus (Name, Positionsargumente, Schlüsselwortargumente) sind.
Sie können ihre „Tupel-Eigenschaft“ nutzen, um die einzelnen Argumente für komplexere Introspektionen und Assertionen zu extrahieren. Die Positionsargumente sind ein Tupel (ein leeres Tupel, wenn keine Positionsargumente vorhanden sind) und die Schlüsselwortargumente sind ein Dictionary.
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True
create_autospec¶
- unittest.mock.create_autospec(spec, spec_set=False, instance=False, **kwargs)¶
Erstellt ein Mock-Objekt unter Verwendung eines anderen Objekts als Spec. Attribute des Mocks verwenden das entsprechende Attribut des Spec-Objekts als ihren Spec.
Funktionen oder Methoden, die gemockt werden, werden auf ihre Argumente überprüft, um sicherzustellen, dass sie mit der korrekten Signatur aufgerufen werden.
Wenn spec_set auf
Truegesetzt ist, löst der Versuch, Attribute festzulegen, die nicht im Spec-Objekt vorhanden sind, einenAttributeErroraus.Wenn eine Klasse als Spec verwendet wird, hat der Rückgabewert des Mocks (die Instanz der Klasse) denselben Spec. Sie können eine Klasse als Spec für ein Instanzobjekt verwenden, indem Sie
instance=Trueübergeben. Der zurückgegebene Mock ist nur aufrufbar, wenn Instanzen des Mocks aufrufbar sind.create_autospec()akzeptiert auch beliebige Schlüsselwortargumente, die an den Konstruktor des erstellten Mocks übergeben werden.
Siehe Autospeccing für Beispiele zur Verwendung von Autospeccing mit create_autospec() und dem autospec-Argument von patch().
Geändert in Version 3.8: create_autospec() gibt nun einen AsyncMock zurück, wenn das Ziel eine asynchrone Funktion ist.
ANY¶
- unittest.mock.ANY¶
Manchmal müssen Sie Assertionen über *einige* der Argumente eines Mock-Aufrufs machen, aber entweder einige Argumente ignorieren oder sie einzeln aus call_args extrahieren und komplexere Assertionen darauf anwenden.
Um bestimmte Argumente zu ignorieren, können Sie Objekte übergeben, die mit *allem* gleich verglichen werden. Aufrufe an assert_called_with() und assert_called_once_with() werden dann erfolgreich sein, egal was übergeben wurde.
>>> mock = Mock(return_value=None)
>>> mock('foo', bar=object())
>>> mock.assert_called_once_with('foo', bar=ANY)
ANY kann auch in Vergleichen mit Aufruflisten wie mock_calls verwendet werden.
>>> m = MagicMock(return_value=None)
>>> m(1)
>>> m(1, 2)
>>> m(object())
>>> m.mock_calls == [call(1), call(1, 2), ANY]
True
ANY ist nicht auf Vergleiche mit Call-Objekten beschränkt und kann daher auch in Test-Assertionen verwendet werden.
class TestStringMethods(unittest.TestCase):
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', ANY])
FILTER_DIR¶
- unittest.mock.FILTER_DIR¶
FILTER_DIR ist eine Modulvariable, die steuert, wie Mock-Objekte auf dir() reagieren. Der Standardwert ist True, was die unten beschriebene Filterung verwendet, um nur nützliche Member anzuzeigen. Wenn Ihnen diese Filterung nicht gefällt oder Sie sie aus Diagnosegründen ausschalten müssen, setzen Sie mock.FILTER_DIR = False.
Mit aktivierter Filterung zeigt dir(some_mock) nur nützliche Attribute an und schließt dynamisch erstellte Attribute ein, die normalerweise nicht angezeigt würden. Wenn der Mock mit einem Spec (oder natürlich Autospec) erstellt wurde, werden alle Attribute des Originals angezeigt, auch wenn sie noch nicht aufgerufen wurden.
>>> dir(Mock())
['assert_any_call',
'assert_called',
'assert_called_once',
'assert_called_once_with',
'assert_called_with',
'assert_has_calls',
'assert_not_called',
'attach_mock',
...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
'AbstractDigestAuthHandler',
'AbstractHTTPHandler',
'BaseHandler',
...
Viele der nicht sehr nützlichen (privat für Mock und nicht für das gemockte Objekt) Attribute, die mit einem Unterstrich oder doppelten Unterstrichen beginnen, wurden aus dem Ergebnis des Aufrufs von dir() auf einem Mock gefiltert. Wenn Ihnen dieses Verhalten missfällt, können Sie es deaktivieren, indem Sie den Modulschalter FILTER_DIR setzen.
>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
'_NonCallableMock__get_side_effect',
'_NonCallableMock__return_value_doc',
'_NonCallableMock__set_return_value',
'_NonCallableMock__set_side_effect',
'__call__',
'__class__',
...
Alternativ können Sie einfach vars(my_mock) (Instanzmitglieder) und dir(type(my_mock)) (Typmitglieder) verwenden, um die Filterung unabhängig von FILTER_DIR zu umgehen.
mock_open¶
- unittest.mock.mock_open(mock=None, read_data=None)¶
Eine Hilfsfunktion zum Erstellen eines Mocks, der die Verwendung von
open()ersetzt. Sie funktioniert für direkt aufgerufenesopen()oder füropen(), das als Kontextmanager verwendet wird.Das Argument mock ist das Mock-Objekt, das konfiguriert werden soll. Wenn es
None(Standard) ist, wird für Sie einMagicMockerstellt, dessen API auf Methoden oder Attribute beschränkt ist, die bei Standard-Dateihandles verfügbar sind.read_data ist eine Zeichenkette für die
read()-,readline()- undreadlines()-Methoden des Dateihandles, die zurückgegeben werden sollen. Aufrufe dieser Methoden entnehmen Daten aus read_data, bis diese aufgebraucht ist. Das Mocking dieser Methoden ist ziemlich simpel: jedes Mal, wenn mock aufgerufen wird, wird read_data vom Anfang an neu gestartet. Wenn Sie mehr Kontrolle über die Daten benötigen, die Sie in den zu testenden Code einspeisen, müssen Sie dieses Mock selbst anpassen. Wenn dies nicht ausreicht, können die In-Memory-Dateisystempakete auf PyPI ein realistisches Dateisystem zum Testen bieten.Geändert in Version 3.4: Unterstützung für
readline()undreadlines()hinzugefügt. Das Mocking vonread()wurde geändert, um read_data zu verbrauchen, anstatt es bei jedem Aufruf zurückzugeben.Geändert in Version 3.5: read_data wird nun bei jedem Aufruf von mock zurückgesetzt.
Geändert in Version 3.8:
__iter__()zur Implementierung hinzugefügt, damit die Iteration (z. B. in for-Schleifen) read_data korrekt verbraucht.
Die Verwendung von open() als Kontextmanager ist eine großartige Möglichkeit, um sicherzustellen, dass Ihre Dateihandles ordnungsgemäß geschlossen werden, und wird immer üblicher.
with open('/some/path', 'w') as f:
f.write('something')
Das Problem ist, dass selbst wenn Sie den Aufruf von open() ausmocken, das *zurückgegebene Objekt* als Kontextmanager verwendet wird (und __enter__() und __exit__() aufgerufen werden).
Das Mocking von Kontextmanagern mit einem MagicMock ist so üblich und umständlich, dass eine Hilfsfunktion nützlich ist.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
Und zum Lesen von Dateien
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Autospeccing¶
Autospeccing basiert auf dem vorhandenen spec Feature von mock. Es beschränkt die API von Mocks auf die API eines Originalobjekts (des Specs), ist aber rekursiv (wird verzögert implementiert), so dass Attribute von Mocks nur die gleiche API wie die Attribute des Specs haben. Darüber hinaus haben gemockte Funktionen/Methoden die gleiche Aufrufsignatur wie das Original, so dass sie einen TypeError auslösen, wenn sie falsch aufgerufen werden.
Bevor ich erkläre, wie Autospeccing funktioniert, hier ist, warum es benötigt wird.
Mock ist ein sehr mächtiges und flexibles Objekt, leidet aber an einem Fehler, der für Mocking allgemein ist. Wenn Sie einen Teil Ihres Codes refaktorisieren, Mitglieder umbenennen und so weiter, werden Tests für Code, der immer noch die *alte API* verwendet, aber Mocks anstelle der realen Objekte verwendet, weiterhin erfolgreich sein. Das bedeutet, dass Ihre Tests alle erfolgreich sein können, obwohl Ihr Code kaputt ist.
Geändert in Version 3.5: Vor 3.5 bestanden Tests mit einem Tippfehler im Wort assert stillschweigend, obwohl sie einen Fehler hätten auslösen sollen. Sie können dieses Verhalten immer noch erreichen, indem Sie unsafe=True an Mock übergeben.
Beachten Sie, dass dies ein weiterer Grund ist, warum Sie Integrations- und Unit-Tests benötigen. Alles isoliert zu testen ist in Ordnung, aber wenn Sie nicht testen, wie Ihre Einheiten "zusammengeschaltet" werden, gibt es immer noch viel Raum für Fehler, die Tests hätten auffangen können.
unittest.mock bietet bereits eine Funktion, die dabei hilft, genannt Speccing. Wenn Sie eine Klasse oder eine Instanz als spec für ein Mock verwenden, können Sie auf dem Mock nur Attribute zugreifen, die auf der realen Klasse existieren.
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
Der Spec gilt nur für das Mock selbst, daher haben wir immer noch das gleiche Problem mit Methoden auf dem Mock.
>>> mock.header_items()
<mock.Mock object at 0x...>
>>> mock.header_items.assret_called_with() # Intentional typo!
Auto-Speccing löst dieses Problem. Sie können entweder autospec=True an patch() / patch.object() übergeben oder die Funktion create_autospec() verwenden, um ein Mock mit einem Spec zu erstellen. Wenn Sie das Argument autospec=True an patch() übergeben, wird das zu ersetzende Objekt als Spec-Objekt verwendet. Da das Speccing "verzögert" erfolgt (der Spec wird erstellt, wenn auf Attribute des Mocks zugegriffen wird), können Sie es mit sehr komplexen oder tief verschachtelten Objekten (wie Modulen, die Module importieren, die Module importieren) ohne größere Leistungseinbußen verwenden.
Hier ist ein Beispiel für die Verwendung.
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
Sie sehen, dass request.Request einen Spec hat. request.Request nimmt zwei Argumente im Konstruktor entgegen (eines davon ist self). Hier ist, was passiert, wenn wir versuchen, es falsch aufzurufen.
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
Der Spec gilt auch für instanziierte Klassen (d. h. den Rückgabewert von gespeccten Mocks).
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request Objekte sind nicht aufrufbar, daher ist der Rückgabewert des Instanziierens unseres gemockten request.Request ein nicht aufrufbares Mock. Mit dem Spec werden Tippfehler in unseren Asserts den korrekten Fehler auslösen.
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
In vielen Fällen müssen Sie nur autospec=True zu Ihren bestehenden patch()-Aufrufen hinzufügen, um dann vor Fehlern aufgrund von Tippfehlern und API-Änderungen geschützt zu sein.
Neben der Verwendung von autospec über patch() gibt es create_autospec() zum direkten Erstellen von Autospecced-Mocks.
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
Dies ist jedoch nicht ohne Vorbehalte und Einschränkungen, weshalb es nicht das Standardverhalten ist. Um zu wissen, welche Attribute auf dem Spec-Objekt verfügbar sind, muss Autospec das Spec introspektieren (Attribute abrufen). Wenn Sie Attribute auf dem Mock durchlaufen, geschieht unter der Haube ein entsprechender Durchlauf durch das Originalobjekt. Wenn Ihre gespeccten Objekte Eigenschaften oder Deskriptoren haben, die Code ausführen können, können Sie Autospec möglicherweise nicht verwenden. Andererseits ist es besser, Ihre Objekte so zu entwerfen, dass die Introspektion sicher ist [4].
Ein ernsteres Problem ist, dass Instanzattribute häufig in der __init__()-Methode erstellt werden und gar nicht auf der Klasse vorhanden sind. autospec kann keine dynamisch erstellten Attribute kennen und beschränkt die API auf sichtbare Attribute.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Es gibt einige verschiedene Möglichkeiten, dieses Problem zu lösen. Die einfachste, aber nicht unbedingt die am wenigsten störende Methode ist, die erforderlichen Attribute einfach nach der Erstellung auf dem Mock festzulegen. Nur weil autospec Ihnen nicht erlaubt, nicht vorhandene Attribute abzurufen, hindert es Sie nicht daran, sie zu setzen.
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
Es gibt eine aggressivere Version von spec und autospec, die verhindert, dass Sie auch nicht vorhandene Attribute setzen. Dies ist nützlich, wenn Sie sicherstellen möchten, dass Ihr Code auch nur gültige Attribute *setzt*, aber offensichtlich verhindert es dieses spezielle Szenario.
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Wahrscheinlich der beste Weg, das Problem zu lösen, ist, Klassenattribute als Standardwerte für Instanzmitglieder hinzuzufügen, die in __init__() initialisiert werden. Beachten Sie, dass, wenn Sie nur Standardattribute in __init__() festlegen, die Bereitstellung über Klassenattribute (die natürlich zwischen Instanzen geteilt werden) auch schneller ist. z. B.
class Something:
a = 33
Dies wirft ein weiteres Problem auf. Es ist relativ üblich, einen Standardwert von None für Mitglieder bereitzustellen, die später ein Objekt eines anderen Typs sein werden. None wäre als Spec nutzlos, da es Ihnen nicht erlaubt, *irgendwelche* Attribute oder Methoden darauf zuzugreifen. Da None *niemals* als Spec nützlich sein wird und wahrscheinlich auf ein Mitglied hinweist, das normalerweise einen anderen Typ hat, verwendet Autospec keinen Spec für Mitglieder, die auf None gesetzt sind. Diese werden einfach gewöhnliche Mocks sein (nun ja - MagicMocks).
>>> class Something:
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>
Wenn Sie Ihre Produktionsklassen nicht zur Aufnahme von Standardwerten ändern möchten, gibt es weitere Optionen. Eine davon ist einfach, eine Instanz als Spec anstelle der Klasse zu verwenden. Die andere ist, eine Unterklasse der Produktionsklasse zu erstellen und die Standardwerte zur Unterklasse hinzuzufügen, ohne die Produktionsklasse zu beeinträchtigen. Beides erfordert, dass Sie ein alternatives Objekt als Spec verwenden. Glücklicherweise unterstützt patch() dies - Sie können das alternative Objekt einfach als autospec-Argument übergeben.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
Dies gilt nur für Klassen oder bereits instanziierte Objekte. Das Aufrufen einer gemockten Klasse zur Erstellung einer gemockten Instanz erstellt *keine* reale Instanz. Es sind nur Attributabfragen - zusammen mit Aufrufen von dir() - die durchgeführt werden.
Sealing Mocks¶
- unittest.mock.seal(mock)¶
Seal deaktiviert die automatische Erstellung von Mocks beim Zugriff auf ein Attribut des zu versiegelnden Mocks oder eines seiner Attribute, die bereits rekursiv Mocks sind.
Wenn eine Mock-Instanz mit einem Namen oder einem Spec einem Attribut zugewiesen wird, wird sie nicht in die Versiegelungskette einbezogen. Dies ermöglicht es, zu verhindern, dass Seal einen Teil des Mock-Objekts repariert.
>>> mock = Mock() >>> mock.submock.attribute1 = 2 >>> mock.not_submock = mock.Mock(name="sample_name") >>> seal(mock) >>> mock.new_attribute # This will raise AttributeError. >>> mock.submock.attribute2 # This will raise AttributeError. >>> mock.not_submock.attribute2 # This won't raise.
Hinzugefügt in Version 3.7.
Reihenfolge der Priorität von side_effect, return_value und wraps¶
Die Reihenfolge ihrer Priorität ist
wraps
Wenn alle drei gesetzt sind, gibt mock den Wert von side_effect zurück, wobei return_value und das gewrappte Objekt vollständig ignoriert werden. Wenn zwei davon gesetzt sind, gibt die höhere Priorität den Wert zurück. Unabhängig von der Reihenfolge, in der sie zuerst gesetzt wurden, bleibt die Reihenfolge der Priorität unverändert.
>>> from unittest.mock import Mock
>>> class Order:
... @staticmethod
... def get_value():
... return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'
Da None der Standardwert von side_effect ist, wird bei Zuweisung seines Wertes zurück zu None die Reihenfolge der Priorität zwischen return_value und dem gewrappten Objekt geprüft, wobei side_effect ignoriert wird.
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
Wenn der von side_effect zurückgegebene Wert DEFAULT ist, wird er ignoriert und die Reihenfolge der Priorität geht zum Nachfolger über, um den zurückzugebenden Wert zu erhalten.
>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'
Wenn Mock ein Objekt wrappt, ist der Standardwert von return_value DEFAULT.
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT
Die Reihenfolge der Priorität wird diesen Wert ignorieren und zum letzten Nachfolger wechseln, der das gewrappte Objekt ist.
Da der eigentliche Aufruf an das gewrappte Objekt gemacht wird, gibt die Erstellung einer Instanz dieses Mocks die reale Instanz der Klasse zurück. Die Positionsargumente, falls vorhanden, die vom gewrappten Objekt benötigt werden, müssen übergeben werden.
>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'
Aber wenn Sie None darauf zuweisen, wird dies nicht ignoriert, da es eine explizite Zuweisung ist. Daher bewegt sich die Reihenfolge der Priorität nicht zum gewrappten Objekt.
>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True
Selbst wenn Sie alle drei gleichzeitig bei der Initialisierung des Mocks setzen, bleibt die Reihenfolge der Priorität gleich.
>>> order_mock = Mock(spec=Order, wraps=Order,
... **{"get_value.side_effect": ["first"],
... "get_value.return_value": "second"}
... )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
Wenn side_effect erschöpft ist, bewirkt die Reihenfolge der Priorität nicht, dass ein Wert von den Nachfolgern bezogen wird. Stattdessen wird die Exception StopIteration ausgelöst.
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
... "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
...
StopIteration