2012-08-06 22 views
47

¿Cómo se burla de una propiedad de solo lectura con mock?¿Cómo burlarse de una propiedad de solo lectura con simulacro?

me trataron:

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock()) 

pero el problema es que a continuación se aplica a todas las instancias de la clase ... que se rompe mis pruebas.

¿Tiene alguna otra idea? No quiero burlarme del objeto completo, solo esta propiedad específica.

Respuesta

37

En realidad, la respuesta fue (como es habitual) en el documentation, es solo que estaba aplicando el parche a la instancia en lugar de la clase cuando seguí su ejemplo.

Aquí es cómo hacerlo:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

En el conjunto de pruebas:

def test(): 
    # Make sure you patch on MyClass, not on a MyClass instance, otherwise 
    # you'll get an AttributeError, because mock is using settattr and 
    # last_transaction is a readonly property so there's no setter. 
    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction: 
     mock_last_transaction.__get__ = mock.Mock(return_value=Transaction()) 
     myclass = MyClass() 
     print myclass.last_transaction 
+6

personas deberían usar el otro ejemplo. 'mock.PropertyMock' es la manera de hacerlo! – vitiral

73

creo que la mejor manera de burlarse de la propiedad como PropertyMock, en lugar de burlarse el método __get__ directamente.

Se indica en el documentation, busque unittest.mock.PropertyMock: Un simulacro destinado a ser utilizado como una propiedad, u otro descriptor, en una clase. PropertyMock proporciona __get__ y __set__ métodos para que pueda especificar un valor de retorno cuando se recupera.

Aquí es cómo:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

def test(unittest.TestCase): 
    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction: 
     mock_last_transaction.return_value = Transaction() 
     myclass = MyClass() 
     print myclass.last_transaction 
     mock_last_transaction.assert_called_once_with() 
+0

Tuve que burlarme de un método de clase decorado como '@ property'. Esta respuesta funcionó para mí cuando la otra respuesta (y otras respuestas en muchas otras preguntas) no funcionó. – AlanSE

+2

esta es la forma en que debería hacerse. Desearía que hubiera una manera de mover la respuesta "aceptada" – vitiral

+1

Creo que incluir el valor de retorno en la llamada del administrador de contexto para ser un poco más limpio: '' ' con mock.patch ('MyClass.last_transaction', new_callable = PropertyMock , return_value = Transaction()): ... '' ' – wodow

3

Probablemente una cuestión de estilo, pero en caso de que prefiera decoradores en las pruebas, @ jamescastlefield de answer se podría cambiar a algo como esto:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

class Test(unittest.TestCase): 
    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock) 
    def test(mock_last_transaction): 
     mock_last_transaction.return_value = Transaction() 
     myclass = MyClass() 
     print myclass.last_transaction 
     mock_last_transaction.assert_called_once_with() 
0

Si Don No quiero probar si se accedió o no a la propiedad burlada, simplemente puede aplicarle el parche esperado return_value.

with mock.patch(MyClass, 'last_transaction', Transaction()): 
    ... 
Cuestiones relacionadas