En mi proyecto actual estoy usando unittest, minimock, nose. En el pasado he usado mucho doctests, pero en proyectos grandes algunas pruebas pueden ser un poco difíciles de manejar, por lo que tiendo a reservar el uso de doctests para funciones más simples.
Si está utilizando setuptools o distribute (que debería estar cambiando a distribuir), puede configurar la nariz como el colector de prueba predeterminado para que pueda ejecutar las pruebas con "prueba setup.py pitón"
setup(name='foo',
...
test_suite='nose.collector',
...
Ahora ejecutar "python setup.py test" invocará a nose, que rastreará su proyecto para buscar cosas que parezcan pruebas y las ejecute, acumulando los resultados. Si también tiene doctests en su proyecto, puede ejecutar nosetest con la opción --with-doctest para habilitar el complemento doctest.
nariz también tiene integración con coverage
nosetests --with-coverage.
También puede utilizar los --cover-html --cover-html-dir opciones para generar un informe de cobertura de HTML para cada módulo, con cada línea del código que no está bajo prueba resaltado. No me obsesionaría demasiado con obtener cobertura para informar el 100% de cobertura de prueba para todos los módulos. Es mejor dejar algún código para las pruebas de integración, que cubriré al final.
Me he convertido en un gran admirador de minimock, ya que hace que el código de prueba con muchas dependencias externas sea realmente fácil. Si bien funciona realmente bien cuando se combina con doctest, se puede usar con cualquier marco de prueba utilizando la clase unittest.TraceTracker. Sin embargo, le recomiendo que evite usarlo para probar todos de su código, ya que aún debe tratar de escribir su código para que cada unidad de traducción pueda probarse aisladamente sin burlarse. A veces eso no es posible sin embargo.
Aquí es un ejemplo (no probado) de dicha prueba usando minimock y unittest:
# tests/test_foo.py
import minimock
import unittest
import foo
class FooTest(unittest2.TestCase):
def setUp(self):
# Track all calls into our mock objects. If we don't use a TraceTracker
# then all output will go to stdout, but we want to capture it.
self.tracker = minimock.TraceTracker()
def tearDown(self):
# Restore all objects in global module state that minimock had
# replaced.
minimock.restore()
def test_bar(self):
# foo.bar invokes urllib2.urlopen, and then calls read() on the
# resultin file object, so we'll use minimock to create a mocked
# urllib2.
urlopen_result = minimock.Mock('urlobject', tracker=self.tracker)
urlopen_result.read = minimock.Mock(
'urlobj.read', tracker=self.tracker, returns='OMG')
foo.urllib2.urlopen = minimock.Mock(
'urllib2.urlopen', tracker=self.tracker, returns=urlopen_result)
# Now when we call foo.bar(URL) and it invokes
# *urllib2.urlopen(URL).read()*, it will not actually send a request
# to URL, but will instead give us back the dummy response body 'OMG',
# which it then returns.
self.assertEquals(foo.bar('http://example.com/foo'), 'OMG')
# Now we can get trace info from minimock to verify that our mocked
# urllib2 was used as intended. self.tracker has traced our calls to
# *urllib2.urlopen()*
minimock.assert_same_trace(self.tracker, """\
Called urllib2.urlopen('http://example.com/foo)
Called urlobj.read()
Called urlobj.close()""")
Las pruebas unitarias no deben ser los únicos tipos de pruebas que escribe sin embargo. Sin duda son útiles y la OMI es extremadamente importante si planea mantener este código por un período prolongado de tiempo. Hacen más fácil la refactorización y ayudan a detectar regresiones, pero en realidad no evalúan la interacción entre varios componentes y cómo interactúan (si lo haces bien).
Cuando empiezo a llegar al punto en el que tengo un producto mayormente terminado con una cobertura de prueba decente que pretendo lanzar, me gusta escribir al menos una prueba de integración que ejecute el programa completo en un entorno aislado.
He tenido mucho éxito con esto en mi proyecto actual. Tenía alrededor del 80% de cobertura de pruebas unitarias, y el resto del código era similar al análisis de argumentos, el envío de comandos y el estado de aplicación de nivel superior, que es difícil de cubrir en pruebas unitarias. Este programa tiene muchas dependencias externas, afecta a una docena de servicios web diferentes e interactúa con aproximadamente 6.000 máquinas en producción, por lo que ejecutarlo de manera aislada resultó algo difícil.
Terminé escribiendo una prueba de integración que engendra un servidor WSGI escrito con eventlet y webob que simula todos los servicios con los que mi programa interactúa en producción. A continuación, el mono de prueba de integración revisa nuestro web service client library para interceptar todas las solicitudes HTTP y enviarlas a la aplicación WSGI. Después de hacerlo, carga un archivo de estado que contiene una instantánea serializada del estado del clúster e invoca la aplicación llamando a su función main(). Ahora todos los servicios externos con los que mi programa interactúa son simulados, de modo que puedo ejecutar mi programa tal como se ejecutaría en producción de manera repetible.
¡Gracias por la respuesta! – bodacydo