Come faccio a prendere in giro una classe con metodi non associati? Ad esempio, questa classe ha un @classmethod
e un @staticmethod
:Come prendere in giro metodi statici e metodi di classe 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
È possibile che questo descrive più o meno la mia domanda. Quello che segue è un esempio funzionante che dimostra le cose che ho provato.
Classe Machine
contiene un'istanza di Calculator
. Sto testando Machine
con una simulazione di Calculator
. Per dimostrare la mia edizione, Machine
chiama i metodi non legate tramite un'istanza di Calculator
e tramite la classe 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
Tutto il codice funzionale sopra funziona bene. La prossima è la parte che non funziona.
Creo una finta di Calculator
da utilizzare nei test 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
Nel test dell'unità sottostante, i metodi vincolati usano MockCalculator
come avevo sperato. Tuttavia, le chiamate a Calculator.increment()
e Calculator.decrement()
usano ancora 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
così cerco di rattoppare Calculator.increment()
e 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)
Anche dopo l'applicazione di patch, i metodi non legate vogliono un'istanza di Calculator
come argomento :
TypeError: unbound method increment_proxy() must be called with Calculator instance as first argument (got int instance instead)
Come faccio mock di metodo di classe 012.347.956,906 milae metodo statico Calculator.decrement()
?
questa è la risposta corretta. [su un'altra domanda] viene discusso il metodo statico rispetto al metodo dei moduli] (http://programmers.stackexchange.com/questions/112137/is-staticmethod-proliferation-a-code-smell) e la conclusione è che i metodi statici sono un odore di codice e un'imitazione di stile Java in cui le definizioni delle funzioni dei moduli non esistono ei metodi statici sono l'unico sostituto. –
Questo non risponde alla domanda. 'staticmethod' è un costrutto Python valido e sapere come deridere queste funzioni è prezioso. "Fai qualcos'altro" non è la risposta giusta, soprattutto considerando che puoi prendere in giro i metodi statici. – AnilRedshift