Ich habe die Scheinbibliothek vorher nicht richtig benutzt, also habe ich einige Nachforschungen angestellt.
Es gibt mehrere Python-Mock-Bibliotheken, aber dieses Mal werde ich mock verwenden. In Python 3.3 oder höher ist es Standard Library und beim Schreiben eines Programms, das nur 3.3 oder höher unterstützt Muss keine externe Bibliothek installieren. Tatsächlich scheint es viele Fälle zu geben, in denen es erst nach 3.3 nicht mehr unterstützt wird. Wird es also üblich sein, mit pip zu installieren?
Scheininstallation
$ pip install mock
Der Punkt ist die mock.Mock-Klasse. Eine Instanz der Mock-Klasse kann aufgerufen werden, und Sie können den Rückgabewert beim Aufrufen festlegen.
Rückgabewerteinstellung
>>> from mock import Mock
>>> m = Mock()
# return_Legen Sie den Rückgabewert im Wertattribut fest
>>> m.return_value = 5
>>> m()
5
Ersetzen Sie die Mock-Instanz durch den auf diese Weise festgelegten Rückgabewert durch die tatsächlich verarbeitete Klassenmethode. Das meiste, was Sie tun, ist dies.
Ein Beispiel wird mit dem folgenden Code gezeigt. Im folgenden Code hängt B # b_test von der Verarbeitung von A # a_test ab. Was ist, wenn ich B # b_test alleine testen möchte?
B#b_Test und A.#a_Testabhängigkeit
class A(object):
def a_test(self):
print('test')
class B(object):
def __init__(self, a_ins):
self.a_ins = a_ins
def b_test(self):
return self.a_ins.a_test()
In diesem Fall kann die Abhängigkeit getrennt werden, indem A # a_test durch die Mock-Instanz ersetzt wird.
Ersatz zum Verspotten
>>> a = A()
>>> a.a_test = Mock()
>>> a.a_test.return_value = 'mocked'
>>> b = B(a)
>>> b.b_test()
'mocked'
Im obigen Beispiel wird die Methode ersetzt. In vielen Fällen möchten Sie jedoch die gesamte Methode auf Instanzbasis und nicht auf Methodenbasis ersetzen.
Geben Sie in diesem Fall beim Erstellen einer Mock-Instanz die Klasse an, die Sie im Argument spec
verspotten möchten.
Ersatz zum Verspotten(Verwenden Sie spec)
>>> a = Mock(spec=A)
>>> a.a_test.return_value = 'mocked_spec'
>>> b = B(a)
>>> b.b_test()
'mocked_spec'
Mock-Aufrufe werden in der Mock-Instanz aufgezeichnet. Mit diesen Informationen können Sie die Beziehungen zwischen Instanzen überprüfen (= ob der Aufruf korrekt ist).
Aufzeichnung eines Scheinanrufs
>>> a = Mock(spec=A)
>>> a.a_test.return_value = 'mocked_spec'
>>> b = B(a)
# Mock#call_args_list:Liste zum Speichern von Aufrufen für diese Mock-Instanz
>>> a.a_test.call_args_list
[]
# Mock#assert_any_call:Bestätigen Sie, ob der entsprechende Mock-Instanzaufruf in der Vergangenheit war.
#In diesem Beispiel gibt es kein Argument, aber in Wirklichkeit ist es möglich, ein beliebiges Argument anzugeben(Geben Sie an, ob mit diesem Argument ein Mock-Instanzaufruf aufgetreten ist)
# (ref. http://www.voidspace.org.uk/python/mock/mock.html)
>>> a.a_test.assert_any_call()
Traceback (most recent call last):
File "<ipython-input-81-ed526bd5ddf7>", line 1, in <module>
a.a_test.assert_any_call()
File "/Users/tatsuro/.venv/py3.3/lib/python3.3/site-packages/mock.py", line 891, in assert_any_call
'%s call not found' % expected_string
AssertionError: a_test() call not found
>>> b.b_test()
'mocked_spec'
# B#b_test()Angerufen über
>>> a.a_test.call_args_list
[call()]
>>> a.a_test.assert_any_call()
>>>
Wenn Sie einen Rückgabewert zurückgeben möchten, können Sie ihn auf "return_value" setzen. Was ist jedoch, wenn Sie den Vorgang des Auslösens einer Ausnahme wie unten gezeigt verspotten möchten?
Verarbeitung, um eine Ausnahme auszulösen
class A(object):
def a_test(self):
raise ValueError
class B(object):
def __init__(self, a_ins):
self.a_ins = a_ins
def b_test(self):
try:
return self.a_ins.a_test()
except ValueError:
print('error handling')
Verwenden Sie in diesem Fall Mock # side_effect
. Wenn Sie hier eine Ausnahme festlegen, können Sie eine Ausnahme auslösen, wenn Sie den Mock aufrufen.
side_Schein-Ausnahmebehandlung durch Effekt
>>> a = Mock(spec=A)
>>> a.a_test.side_effect = ValueError
>>> b = B(a)
>>> b.b_test()
error handling
Hier wird die Ausnahmebehandlung als häufiger Anwendungsfall erwähnt, aber side_effect ist nicht auf die Ausnahmebehandlung spezialisiert. Es ist ein Mechanismus zum "Hinzufügen der erforderlichen Logik beim Aufrufen einer Mock-Instanz", und es ist auch möglich, eine Verarbeitung nur zu einem bestimmten Argument hinzuzufügen und als Rückgabewert zu verwenden, wie unten gezeigt.
Fügen Sie je nach Argument eine andere Verarbeitung hinzu
>>> class B(object):
def __init__(self, a_ins):
self.a_ins = a_ins
def b_test(self, value):
try:
return self.a_ins.a_test(value)
except ValueError:
print('error handling')
>>> def handler(value):
if (value % 2) == 0:
return value * 2
return value
>>> a.a_test.side_effect = handler
>>> b = B(a)
>>> b.b_test(1)
1
>>> b.b_test(2)
4
>>> b.b_test(3)
3
Das Einbeziehen einer zu komplizierten Logik in side_effect scheint jedoch aus Wartungssicht nicht vorzuziehen zu sein. Wenn die Logik kompliziert wird, müssen möglicherweise Maßnahmen wie das Teilen des Testfalls ergriffen werden.
mock hat einen Mechanismus namens "patch", der das Verspotten nur in einem bestimmten Bereich ermöglicht.
patch
kann in Funktionsaufrufen verwendet werden, kann aber auch als Kontextmanager oder Dekorateur behandelt werden.
In vielen Fällen wird es hier behandelt.
Ein Beispiel für die Behandlung als Kontextmanager ist wie folgt. Sie können das Modell mit dem Namen behandeln, der nach als angegeben ist.
Behandle Patch als Kontextmanager
>>> class C(object):
def hoge(self):
return 'hoge'
#Klasse C verspotten nur mit Geltungsbereich unter der with-Anweisung(CMock)Kann verwendet werden
>>> with patch('__main__.C') as CMock:
c = CMock()
c.hoge.return_value = 'test'
print(c.hoge())
...
test
Wenn Sie es als Dekorateur behandeln, ist es wie folgt. Ein Mock wird als letztes Argument der dekorierten Funktion übergeben.
Behandle Patch als Dekorateur für eine Funktion
>>> @patch('__main__.C')
def patched_func(CMock):
c = CMock()
c.hoge.return_value = 'test'
print(c.hoge())
...
>>> patched_func()
test
Es kann nicht nur als Dekorateur für eine Funktion, sondern auch als Dekorateur für eine Klasse behandelt werden.
Viele Testwerkzeuge können Testfälle in Klassen gruppieren (unter der Annahme = 1 Testfall / 1 Methode), sodass auf alle Tests ein gemeinsames Modell angewendet werden kann.
Zu diesem Zeitpunkt muss auf das Vorhandensein von "patch.TEST_PREFIX" geachtet werden. Wenn Sie einen Dekorator für die Klasse angeben, wird der Mock nur für Methoden übergeben, die mit patch.TEST_PREFIX
beginnen.
Daher ist es notwendig, die Testmethode gemäß einer bestimmten Namensregel zu implementieren. (Aber der Standardwert ist'test ', also mach dir keine Sorgen?)
Behandle Patch als Dekorateur für eine Klasse
#Mock wird nur an Methoden mit diesem Präfix übergeben.
>>> patch.TEST_PREFIX
'test'
>>> @patch('__main__.C')
class CTest(object):
def test_c(self, CMock):
c = CMock()
c.hoge.return_value = 'hoge_test'
print(c.hoge())
def notmock(self, CMock):
pass
...
# 'test'Methoden, die mit beginnen. Ein Schein wird übergeben.
>>> CTest().test_c()
hoge_test
# 'test'Eine Methode, die nicht mit beginnt. Da der Mock nicht übergeben wird, tritt aufgrund unzureichender Argumente ein Fehler auf.
>>> CTest().notmock()
Traceback (most recent call last):
File "<ipython-input-189-cee8cb83c7b4>", line 1, in <module>
CTest().notmock()
TypeError: notmock() missing 1 required positional argument: 'CMock'
Recommended Posts