test pruebas ejecutar automatizadas python unit-testing mocking python-mock

ejecutar - pruebas de python



Mejor manera de simular atributos de clase en la prueba unitaria de python (5)

Tengo una clase base que define un atributo de clase y algunas clases secundarias que dependen de él, por ejemplo,

class Base(object): assignment = dict(a=1, b=2, c=3)

Quiero hacer una prueba de unidad de esta clase con diferentes asignaciones , por ejemplo, diccionario vacío, solo elemento, etc. Esto es extremadamente simplificado, por supuesto, no es una cuestión de refactorizar mis clases o pruebas

Las pruebas (pytest) que he encontrado, con el tiempo, ese trabajo son

from .base import Base def test_empty(self): with mock.patch("base.Base.assignment") as a: a.__get__ = mock.Mock(return_value={}) assert len(Base().assignment.values()) == 0 def test_single(self): with mock.patch("base.Base.assignment") as a: a.__get__ = mock.Mock(return_value={''a'':1}) assert len(Base().assignment.values()) == 1

Esto se siente bastante complicado y intrépido, ni siquiera entiendo completamente por qué funciona (aunque estoy familiarizado con los descriptores). ¿La mofa transforma automágicamente los atributos de clase en descriptores?

Una solución que se sentiría más lógica no funciona:

def test_single(self): with mock.patch("base.Base") as a: a.assignment = mock.PropertyMock(return_value={''a'':1}) assert len(Base().assignment.values()) == 1

o solo

def test_single(self): with mock.patch("base.Base") as a: a.assignment = {''a'':1} assert len(Base().assignment.values()) == 1

Otras variantes que he intentado tampoco funcionan (las asignaciones permanecen sin cambios en la prueba).

¿Cuál es la forma correcta de burlarse de un atributo de clase? ¿Hay una forma mejor / más comprensible que la anterior?


Aquí hay un ejemplo de cómo hacer una prueba unitaria de su clase Base :

  • burlándose de múltiples atributos de clase de diferentes tipos (es decir, dict y int )
  • usando el decorador @patch y el framework pytest con python 2.7+ o 3+ .

# -*- coding: utf-8 -*- try: #python 3 from unittest.mock import patch, PropertyMock except ImportError as e: #python 2 from mock import patch, PropertyMock from base import Base @patch(''base.Base.assign_dict'', new_callable=PropertyMock, return_value=dict(a=1, b=2, c=3)) @patch(''base.Base.assign_int'', new_callable=PropertyMock, return_value=9765) def test_type(mock_dict, mock_int): """Test if mocked class attributes have correct types""" assert isinstance(Base().assign_dict, dict) assert isinstance(Base().assign_int , int)


Para mejorar la legibilidad puede usar el decorador @patch :

from mock import patch from unittest import TestCase from base import Base class MyTest(TestCase): @patch(''base.Base.assignment'') def test_empty(self, mock_assignment): # The `mock_assignment` is a MagicMock instance, # you can do whatever you want to it. mock_assignment.__get__.return_value = {} self.assertEqual(len(Base().assignment.values()), 0) # ... and so on

Puede encontrar más detalles en http://www.voidspace.org.uk/python/mock/patch.html#mock.patch .


Si su clase (Cola, por ejemplo) ya se importó dentro de su prueba, y desea parchar MAX_RETRY attr, puede usar @patch.object o simplemente mejor @patch.multiple

from mock import patch, PropertyMock, Mock from somewhere import Queue @patch.multiple(Queue, MAX_RETRY=1, some_class_method=Mock) def test_something(self): do_something() @patch.object(Queue, ''MAX_RETRY'', return_value=1, new_callable=PropertyMock) def test_something(self, _mocked): do_something()


Tal vez me esté perdiendo algo, pero ¿no es esto posible sin usar PropertyMock ?

with mock.patch.object(Base, ''assignment'', {''bucket'': ''head''}): # do stuff


base.Base.assignment simplemente se reemplaza con un objeto Mock . Lo hiciste un descriptor agregando un método __get__ .

Es un poco verboso y un poco innecesario; simplemente puede establecer base.Base.assignment directamente:

def test_empty(self): Base.assignment = {} assert len(Base().assignment.values()) == 0

Esto no es demasiado seguro cuando se usa la concurrencia de prueba, por supuesto.

Para usar un PropertyMock , yo usaría:

with patch(''base.Base.assignment'', new_callable=PropertyMock) as a: a.return_value = {''a'': 1}

o incluso:

with patch(''base.Base.assignment'', new_callable=PropertyMock, return_value={''a'': 1}):