Jak kpić z klasy, która ma niezwiązane metody? Na przykład, ta klasa ma @classmethod
i @staticmethod
:Jak wyśmiewać metody statyczne i metody klasowe w języku Python
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
Powyższy dość dużo opisuje moje pytanie. Poniżej znajduje się działający przykład demonstrujący rzeczy, które próbowałem.
Klasa Machine
zawiera wystąpienie Calculator
. Będę testować Machine
z próbą Calculator
. Do wykazania mój problem, Machine
wywołuje metody niezwiązanych poprzez wystąpienie Calculator
i za pośrednictwem klasy Calculator
:
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
przede wszystkim funkcjonalny kod działa prawidłowo. Dalej jest ta część, która nie działa.
utworzyć próbną Calculator
do wykorzystania w testach 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
W teście jednostkowej poniżej metody bound użyciu MockCalculator
, jak się spodziewano. Jednak rozmowy do Calculator.increment()
i Calculator.decrement()
nadal korzystać 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
Więc staram się załatać Calculator.increment()
i 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)
Nawet po łatanie, metody niezwiązane chcą wystąpienie Calculator
jako argument :
TypeError: unbound method increment_proxy() must be called with Calculator instance as first argument (got int instance instead)
Jak wyśmiewać metodę klasy Calculator.increment()
i metoda statyczna Calculator.decrement()
?
to jest poprawna odpowiedź. [w innym pytaniu] problem metody statycznej vs metody modułu] (http://programmers.stackexchange.com/questions/112137/is-staticmethod-proliferation-a-code-smell) jest omawiany, a wniosek jest taki, że metody statyczne są zapach kodu i imitacja stylu Java, w którym nie istnieją definicje funkcji modułów, a statyczne metody są jedynymi substytutami. –
To nie odpowiada na pytanie. 'staticmethod' jest poprawną konstrukcją Pythona i wiedza o tym, jak kpić z tych funkcji jest cenna. "Zrób coś innego" nie jest właściwą odpowiedzią, szczególnie biorąc pod uwagę, że możesz kpić z metod statycznych. – AnilRedshift