Tiene razón, sus pruebas no deben verificar que el randommódulo está haciendo su trabajo; una prueba de unidad solo debe probar la clase en sí, no cómo interactúa con otro código (que debe probarse por separado).
Por supuesto, es completamente posible que su código use random.randint()incorrectamente; o estás llamando random.randrange(1, self._sides)y tu dado nunca arroja el valor más alto, pero ese sería un tipo diferente de error, no uno que puedas atrapar con una prueba de unidad. En ese caso, su die unidad está funcionando según lo diseñado, pero el diseño en sí era defectuoso.
En este caso, usaría la burla para reemplazar la randint()función, y solo verificaría que se haya llamado correctamente. Python 3.3 y versiones posteriores vienen con el unittest.mockmódulo para manejar este tipo de pruebas, pero puede instalar el mockpaquete externo en versiones anteriores para obtener exactamente la misma funcionalidad
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
Con burlas, su prueba ahora es muy simple; solo hay 2 casos, de verdad. El caso predeterminado para un dado de 6 lados y el caso de lados personalizados.
Hay otras formas de reemplazar temporalmente la randint()función en el espacio de nombres global de Die, pero el mockmódulo lo hace más fácil. El @mock.patchdecorador aquí se aplica a todos los métodos de prueba en el caso de prueba; a cada método de prueba se le pasa un argumento adicional, la random.randint()función simulada, por lo que podemos probar el simulacro para ver si realmente se ha llamado correctamente. El return_valueargumento especifica lo que se devuelve del simulacro cuando se llama, por lo que podemos verificar que el die.roll()método nos haya devuelto el resultado 'aleatorio'.
He usado otra práctica recomendada de pruebas unitarias de Python aquí: importe la clase bajo prueba como parte de la prueba. El _make_onemétodo realiza la importación y la creación de instancias dentro de una prueba , de modo que el módulo de prueba aún se cargará incluso si cometió un error de sintaxis u otro error que evitará que el módulo original se importe.
De esta manera, si cometió un error en el código del módulo, las pruebas aún se ejecutarán; simplemente fallarán y le informarán sobre el error en su código.
Para ser claros, las pruebas anteriores son simplistas en extremo. El objetivo aquí no es probar que random.randint()se ha llamado con los argumentos correctos, por ejemplo. En cambio, el objetivo es probar que la unidad está produciendo los resultados correctos dadas ciertas entradas, donde esas entradas incluyen los resultados de otras unidades que no están bajo prueba. Al burlarse del random.randint()método, puede tomar el control de solo otra entrada a su código.
En las pruebas del mundo real , el código real en su unidad bajo prueba será más complejo; La relación con las entradas pasadas a la API y cómo se invocan otras unidades puede ser interesante aún, y la burla le dará acceso a resultados intermedios, así como también le permitirá establecer los valores de retorno para esas llamadas.
Por ejemplo, en el código que autentica a los usuarios contra un servicio OAuth2 de terceros (una interacción de varias etapas), desea probar que su código está pasando los datos correctos a ese servicio de terceros y le permite simular diferentes respuestas de error que El servicio de terceros volvería, permitiéndole simular diferentes escenarios sin tener que construir un servidor OAuth2 completo usted mismo. Aquí es importante probar que la información de una primera respuesta se ha manejado correctamente y se ha pasado a una llamada de segunda etapa, por lo que desea ver que el servicio simulado se llama correctamente.