¿A dónde van las pruebas unitarias de Python?


490

Si está escribiendo una biblioteca o una aplicación, ¿a dónde van los archivos de prueba de la unidad?

Es bueno separar los archivos de prueba del código principal de la aplicación, pero es incómodo colocarlos en un subdirectorio de "pruebas" dentro del directorio raíz de la aplicación, porque hace que sea más difícil importar los módulos que probará.

¿Hay una mejor práctica aquí?


44
Hay muchas respuestas aceptables a esta pregunta y eso hace que no sea una pregunta para un sitio de preguntas y respuestas. La gente necesita entender que no todas las preguntas útiles son útiles en SO. Los formadores eligen eso y debemos seguir sus reglas.
Wouter J

121
Las personas también deben entender que cuando las páginas SO terminan como un éxito para una búsqueda en Google, tal vez sea mejor seguir la corriente en lugar de apegarse a la doctrina oficial de SO.
Christopher Barber

66
La gente de @ChristopherBarber también necesita entender que las decisiones y el cierre agresivo es lo que terminó ganando una reputación TAN horrenda como un sitio que preferiría no hacer preguntas a menos que realmente no tenga una mejor opción.
Stefano Borini

Respuestas:


200

Para un archivo module.py, normalmente se debe llamar a la prueba unitaria test_module.py, siguiendo las convenciones de nomenclatura Pythonic.

Hay varios lugares comúnmente aceptados para poner test_module.py:

  1. En el mismo directorio que module.py.
  2. En ../tests/test_module.py(al mismo nivel que el directorio de código).
  3. En tests/test_module.py(un nivel bajo el directorio de código).

Prefiero # 1 por su simplicidad de encontrar las pruebas e importarlas. Cualquiera sea el sistema de compilación que esté utilizando, puede configurarse fácilmente para ejecutar archivos a partir de test_. En realidad, el patrón predeterminado unittestutilizado para el descubrimiento de prueba estest*.py .


12
El protocolo load_tests busca archivos denominados test * .py de forma predeterminada. Además, este resultado superior de Google y esta documentación de prueba de unidad utilizan el formato test_module.py.
dfarrell07

66
El uso de foo_test.py puede guardar una o dos pulsaciones de teclas al usar la finalización de tabulación ya que no tiene un montón de archivos que comienzan con 'test_'.
enebro-

11
@juniper, siguiendo tus pensamientos module_test aparecería en la finalización automática cuando estés codificando. Eso puede ser confuso o molesto.
Medeiros

11
Al implementar código, no queremos implementar las pruebas en nuestras ubicaciones de producción. Por lo tanto, tenerlos en un directorio './tests/test_blah.py' es fácil de extraer cuando hacemos implementaciones. TAMBIÉN, algunas pruebas toman archivos de datos de muestra, y tenerlos en un directorio de prueba es vital para que no implementemos datos de prueba.
Kevin J. Rice,

1
@ KevinJ.Rice ¿No deberías probar que el código funciona en tus ubicaciones de producción?
endolito

67

Solo 1 archivo de prueba

Si solo tiene 1 archivo de prueba, se recomienda colocarlo en un directorio de nivel superior:

module/
    lib/
        __init__.py
        module.py
    test.py

Ejecute la prueba en CLI

python test.py

Muchos archivos de prueba

Si tiene muchos archivos de prueba, póngalo en una testscarpeta:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

if __name__ == '__main__':
    unittest.main()

Ejecute la prueba en CLI

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

Utilizar unittest discovery

unittest discovery encontrará todas las pruebas en la carpeta del paquete.

Crear una carpeta __init__.pyentests/

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

Ejecute la prueba en CLI

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

Referencia

Marco de prueba de la unidad


51

Una práctica común es colocar el directorio de pruebas en el mismo directorio principal que su módulo / paquete. Entonces, si su módulo se llamara foo.py, su diseño de directorio se vería así:

parent_dir/
  foo.py
  tests/

Por supuesto, no hay una sola forma de hacerlo. También puede hacer un subdirectorio de pruebas e importar el módulo usando la importación absoluta .

Donde sea que coloque sus pruebas, le recomendaría que use la nariz para ejecutarlas. Nariz busca a través de sus directorios para las pruebas. De esta manera, puede poner pruebas donde sea que tengan más sentido organizacionalmente.


16
Me gustaría hacer esto, pero no puedo hacer que funcione. Para ejecutar las pruebas, estoy en parent_dir y escribo: "python tests \ foo_test.py", y en foo_test.py: "from ..foo importa esto, eso, el otro" Eso falla con: "ValueError: Intentado importación relativa en no paquete "Tanto parent_dir como las pruebas tienen un init .py, así que no estoy seguro de por qué no son paquetes. Sospecho que es porque el script de nivel superior que ejecuta desde la línea de comandos no puede considerarse (parte de) un paquete, incluso si está en un directorio con un init .py. Entonces, ¿cómo ejecuto las pruebas? Hoy estoy trabajando en Windows, bendiga mis calcetines de algodón.
Jonathan Hartley

44
La mejor manera, según he descubierto, de ejecutar pruebas unitarias es instalar su biblioteca / programa y luego ejecutar pruebas unitarias con nariz. Recomendaría virtualenv y virtualenvwrapper para hacer esto mucho más fácil.
Cristian

@Tartley: necesita un archivo init .py en su directorio 'tests' para que funcionen las importaciones de absolución. Tengo este método trabajando con la nariz, así que no estoy seguro de por qué estás teniendo problemas.
cmcginty

44
Gracias Casey, pero tengo un archivo init .py en todos los directorios relevantes. No sé qué hago mal, pero tengo este problema en todos mis proyectos de Python y no puedo entender por qué nadie más lo hace. Oh querido.
Jonathan Hartley

8
Una solución para mi problema, con Python2.6 o posterior, es ejecutar las pruebas desde la raíz de su proyecto usando: python -m project.package.tests.module_tests (en lugar de python project / package / tests / module_tests.py) . Esto coloca el directorio del módulo de prueba en la ruta, por lo que las pruebas pueden hacer una importación relativa a su directorio principal para obtener el módulo bajo prueba.
Jonathan Hartley

32

Tuvimos la misma pregunta al escribir Pythoscope ( https://pypi.org/project/pythoscope/ ), que genera pruebas unitarias para los programas de Python. Encuestamos a las personas en las pruebas en la lista de Python antes de elegir un directorio, había muchas opiniones diferentes. Al final, elegimos poner un directorio de "pruebas" en el mismo directorio que el código fuente. En ese directorio generamos un archivo de prueba para cada módulo en el directorio padre.


2
¡Increíble! Acabo de ejecutar Pythoscope (gran logotipo), en un código heredado y fue increíble. Mejores prácticas instantáneas y puede hacer que otros desarrolladores completen los comprobantes de prueba que ahora fallan. ¿Ahora para conectarlo al bambú? :)
Será

27

También tiendo a poner mis pruebas unitarias en el archivo, como señala Jeremy Cantrell arriba, aunque tiendo a no poner la función de prueba en el cuerpo principal, sino poner todo en un

if __name__ == '__main__':
   do tests...

bloquear. Esto termina agregando documentación al archivo como 'código de ejemplo' sobre cómo usar el archivo python que está probando.

Debo agregar, tiendo a escribir módulos / clases muy ajustados. Si sus módulos requieren un gran número de pruebas, puede colocarlos en otro, pero aun así, aún agregaría:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

Esto permite que cualquiera que lea su código fuente sepa dónde buscar el código de prueba.


"como Jeremy Cantrell señala arriba" ¿dónde?
endolito

Me gusta esta forma de trabajar. El más simple de los editores se puede configurar para ejecutar su archivo con una tecla de acceso rápido, por lo que cuando esté viendo el código, puede ejecutar las pruebas en un instante. Desafortunadamente, en otros lenguajes que no sean Python, esto puede verse horrible, por lo que si estás en una tienda de C ++ o Java, tus colegas pueden desaprobar esto. Tampoco funciona bien con las herramientas de cobertura de código.
Keeely

19

De vez en cuando me encuentro revisando el tema de la colocación de la prueba, y cada vez la mayoría recomienda una estructura de carpetas separada al lado del código de la biblioteca, pero encuentro que cada vez los argumentos son los mismos y no son tan convincentes. Termino colocando mis módulos de prueba en algún lugar al lado de los módulos principales.

La razón principal para hacer esto es: refactorización .

Cuando muevo cosas, quiero que los módulos de prueba se muevan con el código; Es fácil perder las pruebas si están en un árbol separado. Seamos honestos, tarde o temprano terminas con una estructura de carpetas totalmente diferente, como django , flask y muchos otros. Lo cual está bien si no te importa.

La pregunta principal que debe hacerse es esta:

¿Estoy escribiendo:

  • a) biblioteca reutilizable o
  • b) construyendo un proyecto que agrupa algunos módulos semi-separados?

Si un:

Una carpeta separada y el esfuerzo adicional para mantener su estructura pueden ser más adecuados. Nadie se quejará de que sus pruebas se implementen en producción .

Pero también es tan fácil excluir las pruebas de ser distribuidas cuando se mezclan con las carpetas principales; pon esto en setup.py :

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

Si b:

Puede desear, como todos nosotros, que esté escribiendo bibliotecas reutilizables, pero la mayoría de las veces su vida está ligada a la vida del proyecto. La capacidad de mantener fácilmente su proyecto debe ser una prioridad.

Luego, si hizo un buen trabajo y su módulo se adapta bien a otro proyecto, probablemente se copiará, no se bifurcará o se convertirá en una biblioteca separada, en este nuevo proyecto y se moverán las pruebas que se encuentran junto a él en la misma estructura de carpetas es fácil en comparación con pescar pruebas en un desastre en el que se había convertido una carpeta de prueba separada. (Puede argumentar que no debería ser un desastre en primer lugar, pero seamos realistas aquí).

Entonces, la elección sigue siendo suya, pero diría que con las pruebas confusas se logran las mismas cosas que con una carpeta separada, pero con menos esfuerzo para mantener las cosas ordenadas.


1
¿Cuál es el problema con la implementación de pruebas en producción, en realidad? en mi experiencia, es muy útil: permite a los usuarios ejecutar pruebas en su entorno ... cuando recibe informes de errores, puede pedirle al usuario que ejecute el conjunto de pruebas para asegurarse de que todo esté bien allí y envíe parches para la prueba Suite directamente ... además, no es porque usted pone sus pruebas en module.tests que va a terminar en la producción, a menos que usted hizo algo mal en su archivo de configuración ...
anarcat

2
Como escribí en mi respuesta, generalmente no separo las pruebas. Pero. Poner pruebas en el paquete de distribución puede llevar a ejecutar cosas que normalmente no desearía en el entorno de producción (por ejemplo, algún código de nivel de módulo). Por supuesto, depende de cómo se escriban las pruebas, pero para estar seguro de que las deje, nadie ejecutará algo dañino por error. No estoy en contra de incluir pruebas en las distribuciones, pero entiendo que, como regla general, es más seguro. Y ponerlos en una carpeta separada hace que sea muy fácil no incluirlos en dist.
Janusz Skonieczny

15

Utilizo un tests/directorio y luego importo los módulos de aplicación principales utilizando importaciones relativas. Entonces, en MyApp / tests / foo.py, podría haber:

from .. import foo

para importar el MyApp.foomódulo.


55
"ValueError: intento de importación relativa en un paquete"
cprn

12

No creo que haya una "mejor práctica" establecida.

Puse mis pruebas en otro directorio fuera del código de la aplicación. Luego agrego el directorio principal de la aplicación a sys.path (que le permite importar los módulos desde cualquier lugar) en mi script de ejecución de prueba (que también hace algunas otras cosas) antes de ejecutar todas las pruebas. De esta manera, nunca tengo que eliminar el directorio de pruebas del código principal cuando lo libero, lo que me ahorra tiempo y esfuerzo, aunque sea una cantidad muy pequeña.


3
Luego agrego el directorio principal de la aplicación a sys.path. El problema es si sus pruebas contienen algunos módulos auxiliares (como el servidor web de prueba) y necesita importar dichos módulos auxiliares desde sus pruebas adecuadas.
Piotr Dobrogost el

Esto es lo que parece para mí:os.sys.path.append(os.dirname('..'))
Matt M.

11

Desde mi experiencia en el desarrollo de marcos de prueba en Python, sugeriría colocar las pruebas unitarias de python en un directorio separado. Mantener una estructura de directorio simétrica. Esto sería útil para empaquetar solo las bibliotecas principales y no empaquetar las pruebas unitarias. A continuación se implementa a través de un diagrama esquemático.

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

De esta manera, cuando empaqueta estas bibliotecas utilizando un rpm, puede empacar los módulos de la biblioteca principal (solo). Esto ayuda a la mantenibilidad, particularmente en entornos ágiles.


1
Me di cuenta de que una ventaja potencial de este enfoque es que las pruebas se pueden desarrollar (y tal vez incluso controlar la versión) como una aplicación independiente propia. Por supuesto, para cada ventaja hay una desventaja: mantener la simetría es básicamente hacer "contabilidad de doble entrada" y hace que la refactorización sea más una tarea.
Jasha

Pregunta de seguimiento suave: ¿por qué el entorno ágil en particular es muy adecuado para este enfoque? (es decir, ¿qué pasa con el flujo de trabajo ágil hace que una estructura de directorio simétrica sea tan beneficiosa?)
Jasha

11

Le recomiendo que revise algunos proyectos principales de Python en GitHub y obtenga algunas ideas.

Cuando su código se hace más grande y agrega más bibliotecas, es mejor crear una carpeta de prueba en el mismo directorio que tiene setup.py y reflejar la estructura de directorios de su proyecto para cada tipo de prueba (unittest, integración, ...)

Por ejemplo, si tiene una estructura de directorio como:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

Después de agregar la carpeta de prueba, tendrá una estructura de directorios como:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

Muchos paquetes de Python escritos correctamente usan la misma estructura. Un muy buen ejemplo es el paquete Boto. Consulta https://github.com/boto/boto


1
Se recomienda utilizar "pruebas" en lugar de "prueba", porque "prueba" es un módulo incorporado en Python. docs.python.org/2/library/test.html
brodul

No siempre ... por ejemplo lo matplotlibtiene en matplotlib/lib/matplotlib/tests( github.com/matplotlib/matplotlib/tree/… ), lo sklearntiene en scikitelearn/sklearn/tests( github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests )
alpha_989

7

Cómo lo hago...

Estructura de carpetas:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py apunta a src / como la ubicación que contiene los módulos de mis proyectos, luego ejecuto:

setup.py develop

Lo que agrega mi proyecto en paquetes de sitio, apuntando a mi copia de trabajo. Para ejecutar mis pruebas uso:

setup.py tests

Usando cualquier corredor de prueba que haya configurado.


Parece que ha anulado el término "módulo". La mayoría de los programadores de Python probablemente pensarían que el módulo es el archivo que usted llamó code.py. Tendría más sentido llamar al directorio de nivel superior "proyecto".
blokeley

4

Prefiero el directorio de pruebas de nivel superior. Esto significa que las importaciones se vuelven un poco más difíciles. Para eso tengo dos soluciones:

  1. Usa herramientas de configuración. A continuación, puede pasar test_suite='tests.runalltests.suite'a setup(), y puede ejecutar las pruebas simplemente:python setup.py test
  2. Configure PYTHONPATH cuando ejecute las pruebas: PYTHONPATH=. python tests/runalltests.py

Así es como el código en M2Crypto admite esas cosas:

Si prefiere realizar pruebas con pruebas nasales, es posible que deba hacer algo un poco diferente.


3

Usamos

app/src/code.py
app/testing/code_test.py 
app/docs/..

En cada archivo de prueba insertamos ../src/en sys.path. No es la mejor solución, pero funciona. Creo que sería genial si alguien apareciera con algo como Maven en Java que le brinde convenciones estándar que simplemente funcionen, sin importar en qué proyecto trabaje.


1

Si las pruebas son simples, simplemente colóquelas en la cadena de documentación; la mayoría de los marcos de prueba para Python podrán usar eso:

>>> import module
>>> module.method('test')
'testresult'

Para otras pruebas más complicadas, las pondría en ../tests/test_module.pyo dentro tests/test_module.py.


1

En C #, generalmente separé las pruebas en un ensamblaje separado.

En Python, hasta ahora, he tendido a escribir doctests, donde la prueba está en la cadena de documentación de una función, o ponerlos en el if __name__ == "__main__"bloque en la parte inferior del módulo.


0

Al escribir un paquete llamado "foo", pondré pruebas unitarias en un paquete separado "foo_test". Los módulos y subpaquetes tendrán el mismo nombre que el módulo de paquete SUT. Por ejemplo, las pruebas para un módulo foo.xy se encuentran en foo_test.xy. Los archivos __init__.py de cada paquete de prueba contienen un conjunto AllTests que incluye todos los conjuntos de pruebas del paquete. setuptools proporciona una manera conveniente de especificar el paquete de prueba principal, de modo que después de "python setup.py desarrollo" puede usar "python setup.py test" o "python setup.py test -s foo_test.x.SomeTestSuite" Solo una suite específica.


0

Puse mis pruebas en el mismo directorio que el código bajo prueba (CUT); para foo.pylas pruebas será en foo_ut.pyo similar. (Modifico el proceso de descubrimiento de prueba para encontrarlos).

Esto coloca las pruebas justo al lado del código en una lista de directorios, lo que hace obvio que las pruebas están allí, y hace que abrir las pruebas sea lo más fácil posible cuando están en un archivo separado. (Para editores de línea de comandos, vim foo*y cuando use un navegador de sistema de archivos gráfico, simplemente haga clic en el archivo CUT y luego en el archivo de prueba adyacente).

Como otros han señalado , esto también hace que sea más fácil refactorizar y extraer el código para usarlo en otro lugar en caso de que sea necesario.

Realmente no me gusta la idea de poner pruebas en un árbol de directorios completamente diferente; ¿Por qué hacer que sea más difícil de lo necesario para los desarrolladores abrir las pruebas cuando abren el archivo con el CUT? No es que la gran mayoría de los desarrolladores estén tan interesados ​​en escribir o ajustar pruebas que ignorarán cualquier barrera para hacerlo, en lugar de usar la barrera como excusa. (Todo lo contrario, en mi experiencia; incluso cuando lo haces lo más fácil posible, conozco a muchos desarrolladores que no pueden molestarse en escribir pruebas).


-2

Recientemente comencé a programar en Python, por lo que aún no he tenido la oportunidad de encontrar las mejores prácticas. Pero, he escrito un módulo que va y encuentra todas las pruebas y las ejecuta.

Así que tengo:

aplicación /
 appfile.py
prueba/
 appfileTest.py

Tendré que ver cómo va a medida que avance en proyectos más grandes.


44
Hola, el camelCase es un poco raro en el mundo de las pitones.
Stuart Axon
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.