Wie mock Python statische Methoden und Klassenmethoden
Wie kann ich simulieren Sie eine Klasse, die ungebundene Methoden? Zum Beispiel, diese Klasse hat eine @classmethod
und ein @staticmethod
:
class Calculator(object):
def __init__(self, multiplier):
self._multiplier = multiplier
def multiply(self, n):
return self._multiplier * n
@classmethod
def increment(cls, n):
return n + 1
@staticmethod
def decrement(n):
return n - 1
calculator = Calculator(2)
assert calculator.multiply(3) == 6
assert calculator.increment(3) == 4
assert calculator.decrement(3) == 2
assert Calculator.increment(3) == 4
assert Calculator.decrement(3) == 2
Oben ziemlich genau beschreibt meine Frage. Das folgende ist ein Beispiel, das veranschaulicht, die Dinge, die ich ausprobiert habe.
Klasse Machine
enthält eine Instanz von Calculator
. Ich werde testen Machine
mit einem mock Calculator
. Um zu demonstrieren, dass mein Problem Machine
ruft die ungebundene Methoden über eine Instanz von Calculator
und über die Calculator
Klasse:
class Machine(object):
def __init__(self, calculator):
self._calculator = calculator
def mult(self, n):
return self._calculator.multiply(n)
def incr_bound(self, n):
return self._calculator.increment(n)
def decr_bound(self, n):
return self._calculator.decrement(n)
def incr_unbound(self, n):
return Calculator.increment(n)
def decr_unbound(self, n):
return Calculator.decrement(n)
machine = Machine(Calculator(3))
assert machine.mult(3) == 9
assert machine.incr_bound(3) == 4
assert machine.incr_unbound(3) == 4
assert machine.decr_bound(3) == 2
assert machine.decr_unbound(3) == 2
Alle funktionalen code oben funktioniert einwandfrei. Als Nächstes ist der Teil, der nicht funktioniert.
Ich erstelle ein mock Calculator
für die Verwendung in Tests Machine
:
from mock import Mock
def MockCalculator(multiplier):
mock = Mock(spec=Calculator, name='MockCalculator')
def multiply_proxy(n):
'''Multiply by 2*multiplier instead so we can see the difference'''
return 2 * multiplier * n
mock.multiply = multiply_proxy
def increment_proxy(n):
'''Increment by 2 instead of 1 so we can see the difference'''
return n + 2
mock.increment = increment_proxy
def decrement_proxy(n):
'''Decrement by 2 instead of 1 so we can see the difference'''
return n - 2
mock.decrement = decrement_proxy
return mock
In der unit-test unter die gebundenen Methoden verwenden MockCalculator
wie ich gehofft hatte. Jedoch, die Anrufe zu Calculator.increment()
und Calculator.decrement()
noch verwenden Calculator
:
import unittest
class TestMachine(unittest.TestCase):
def test_bound(self):
'''The bound methods of Calculator are replaced with MockCalculator'''
machine = Machine(MockCalculator(3))
self.assertEqual(machine.mult(3), 18)
self.assertEqual(machine.incr_bound(3), 5)
self.assertEqual(machine.decr_bound(3), 1)
def test_unbound(self):
'''Machine.incr_unbound() and Machine.decr_unbound() are still using
Calculator.increment() and Calculator.decrement(n), which is wrong.
'''
machine = Machine(MockCalculator(3))
self.assertEqual(machine.incr_unbound(3), 4) # I wish this was 5
self.assertEqual(machine.decr_unbound(3), 2) # I wish this was 1
So, ich versuche den patch Calculator.increment()
und Calculator.decrement()
:
def MockCalculatorImproved(multiplier):
mock = Mock(spec=Calculator, name='MockCalculatorImproved')
def multiply_proxy(n):
'''Multiply by 2*multiplier instead of multiplier so we can see the difference'''
return 2 * multiplier * n
mock.multiply = multiply_proxy
return mock
def increment_proxy(n):
'''Increment by 2 instead of 1 so we can see the difference'''
return n + 2
def decrement_proxy(n):
'''Decrement by 2 instead of 1 so we can see the difference'''
return n - 2
from mock import patch
@patch.object(Calculator, 'increment', increment_proxy)
@patch.object(Calculator, 'decrement', decrement_proxy)
class TestMachineImproved(unittest.TestCase):
def test_bound(self):
'''The bound methods of Calculator are replaced with MockCalculator'''
machine = Machine(MockCalculatorImproved(3))
self.assertEqual(machine.mult(3), 18)
self.assertEqual(machine.incr_bound(3), 5)
self.assertEqual(machine.decr_bound(3), 1)
def test_unbound(self):
'''machine.incr_unbound() and Machine.decr_unbound() should use
increment_proxy() and decrement_proxy(n).
'''
machine = Machine(MockCalculatorImproved(3))
self.assertEqual(machine.incr_unbound(3), 5)
self.assertEqual(machine.decr_unbound(3), 1)
Sogar nach dem patchen, ungebundene Methoden soll eine Instanz von Calculator
als argument:
TypeError: unbound method increment_proxy() muss aufgerufen werden, mit Taschenrechner-Beispiel als erstes argument (got int-Instanz statt)
Wie kann ich die mock-out-Methode der Klasse Calculator.increment()
und statische Methode Calculator.decrement()
?
InformationsquelleAutor John McGehee | 2014-04-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Waren Sie patchen das falsche Objekt. Sie müssen patch der
Calculator
von derMachine
Klasse, nicht die AllgemeineCalculator
Klasse. Lesen Sie mehr über es hier.InformationsquelleAutor Alex Palcuie
C# -, Java-und C++ - Programmierer neigen dazu, übernutzung Klasse und statische Methoden in Python. Die Pythonic Ansatz ist die Verwendung von Modul-Funktionen.
Also erstmal, hier ist die überarbeitete software im test, mit Methoden
increment()
unddecrement()
als Modul-Funktionen. Die Schnittstelle ändert sich, aber die Funktionalität ist die gleiche:Hinzufügen von Funktionen
increment_mock()
unddecrement_mock()
zu verspottenincrement()
unddecrement()
:Und nun zum guten Teil. Patch
increment()
unddecrement()
zu ersetzen, die Ihnen mit Ihren verspottet:Dies beantwortet nicht die Frage.
staticmethod
ist ein Gültiger python-Konstrukt, und wissen, wie Sie zu verspotten jene Funktionen, die wertvoll ist. "Etwas anderes tun" ist nicht die richtige Antwort-vor allem, wenn man bedenkt Sie können mock staticmethods.Lesen Sie diese und erkannte auch ich war übernutzung statische Methode, wenn ich hätte mit Modul-Funktionen. Obwohl dies scheint zu off-topic für die Frage, es hat mir wirklich geholfen, vielen Dank.
InformationsquelleAutor John McGehee