example - Mejores prácticas de PHPUnit para organizar pruebas
phpunit tutorial español (2)
En este momento voy a empezar de cero con las pruebas de phpunit para un proyecto. Así que estaba investigando algunos proyectos (como Zend) para ver cómo están haciendo las cosas y cómo organizan sus pruebas.
La mayoría de las cosas son bastante claras, lo único que tengo algunos problemas es cómo organizar los bancos de pruebas de manera adecuada. Zend tiene un AllTests.php desde el que carga otras suites de prueba.
Duro mirando la clase, está usando PHPUnit_Framework_TestSuite
para crear un objeto suite y luego agregarle las otras suites, pero si miro en los documentos PHPUnit para organizar pruebas en versiones PHPUnit después de 3.4, solo hay una descripción para XML o FileHierarchy. El que usa las clases para organizar las pruebas fue eliminado.
No he encontrado nada de que este método esté en desuso y proyectos como Zend todavía lo están usando.
Pero si está en desuso, ¿cómo podría organizar las pruebas en la misma estructura con la configuración xml? Ejecutar todas las pruebas no es problema, pero ¿cómo organizaría las pruebas (en el xml) si solo quisiera ejecutar algunas pruebas? ¿Tal vez crear varios xmls donde solo especifique unas pocas pruebas / suites de prueba para ejecutar?
Por lo tanto, si quisiera probar solo el módulo1 y el módulo2 de la aplicación, tendría un xml adicional para cada uno y definiría suites de prueba solo para aquellos módulos (clases utilizadas por el módulo) en él. ¿Y también uno que define un conjunto de pruebas para todas las pruebas?
¿O sería mejor usar la anotación @group
en las pruebas específicas para marcar que sean para el módulo1 o el módulo2?
Gracias de antemano por señalarme algunas buenas prácticas.
Comenzaré relacionando el manual y luego veré lo que he visto y oído en el campo.
Organización de suites de prueba phpunit
Organización de carpeta de módulo / prueba en el sistema de archivos
Mi enfoque recomendado es combinar el sistema de archivos con una configuración xml.
tests/
/ unit/
| - module1
| - module2
- integration/
- functional/
con phpunit.xml
con un simple:
<testsuites>
<testsuite name="My whole project">
<directory>tests</directory>
</testsuite>
</testsuites>
Puede dividir las salas de pruebas si lo desea, pero ese es un proyecto para elegir el proyecto.
Al ejecutar phpunit
, se ejecutarán TODAS las pruebas y ejecutar phpunit tests/unit/module1
ejecutará todas las pruebas de module1.
Organización de la carpeta "unidad"
El enfoque más común aquí es reflejar su estructura source/
directorio en su estructura de tests/unit/
carpeta.
Tienes una TestClass por ProductionClass de todos modos, así que es un buen enfoque en mi libro.
En la organización de archivos
- Una clase por archivo.
De todos modos no funcionará si tiene más de una clase de prueba en un archivo, así que evite esa trampa.
- No tiene un espacio de nombres de prueba
Simplemente hace que escribir la prueba sea más detallado ya que necesita una declaración de uso adicional, así que diría que testClass debería ir en el mismo espacio de nombres que la clase de producción, pero eso no es nada que PHPUnit te obligue a hacer. Acabo de descubrir que es más fácil sin inconvenientes.
Ejecutando solo algunas pruebas
Por ejemplo, phpunit --filter Factory
ejecuta todos los FactoryTests mientras que phpunit tests/unit/logger/
ejecuta todo lo relacionado con el registro.
Puedes usar etiquetas @group
para algo como números de edición, historias o algo así, pero para "módulos" usaría el diseño de la carpeta.
Múltiples archivos xml
Puede ser útil crear múltiples archivos xml si desea tener:
- uno sin cobertura de código
- uno solo para las pruebas unitarias (pero no para las pruebas funcionales o de integración o de larga ejecución)
- otros casos comunes de "filtro"
- PHPBB3 por ejemplo hace eso por
their phpunit.xmls
Cobertura de código para sus pruebas
Como está relacionado con comenzar un nuevo proyecto con pruebas:
- Mi sugerencia es usar
@covers
etiquetas@covers
como se describe en mi blog (Solo para pruebas unitarias, siempre cubra todas las funciones no públicas, siempre use etiquetas de portada. - No genere cobertura para sus pruebas de integración. Te da una falsa sensación de seguridad.
- Utilice siempre la lista blanca para incluir todo su código de producción para que los números no le mientan.
Autoloading y bootstrapping tus pruebas
No necesita ningún tipo de carga automática para sus pruebas. PHPUnit se encargará de eso.
Use el <phpunit bootstrap="file">
para especificar su bootstrap de prueba. tests/bootstrap.php
es un buen lugar para ponerlo. Allí puede configurar el autocargador de aplicaciones, etc. (o puede llamar al arranque de sus aplicaciones).
Resumen
- Usa la configuración xml para prácticamente todo
- Unidad separada y pruebas de integración
- Las carpetas de prueba de tu unidad deben reflejar la estructura de tu carpeta de aplicaciones
- Para ejecutar solo pruebas específicas, use
phpunit --filter
ophpunit tests/unit/module1
- Utilice el modo
strict
desde el principio y nunca lo apague.
Ejemplos de proyectos para mirar
- Proyecto de ejemplo de la "cuenta bancaria" de Sebastian Bergmanns
- phpBB3 Aun así tienen que luchar contra algunos con su legado;)
- Symfony2
- Doctrine2
Estructura básica del directorio :
He estado experimentando con mantener el código de prueba justo al lado del código que se está probando, literalmente en el mismo directorio con un nombre de archivo ligeramente diferente del archivo con el código que está probando. Hasta ahora me gusta este enfoque. La idea es que no tenga que gastar tiempo y energía manteniendo la estructura del directorio sincronizada entre su código y su código de prueba. Por lo tanto, si cambia el nombre del directorio en el que se encuentra el código, entonces no necesita ir y buscar y cambiar el nombre del directorio para el código de prueba. Esto también hace que pases menos tiempo buscando el código de prueba que acompaña a algún código, ya que está justo al lado. Esto hace que sea menos complicado crear el archivo con el código de prueba para empezar, ya que no tiene que encontrar primero el directorio con las pruebas, posiblemente crear un nuevo directorio para que coincida con el que está creando pruebas, y luego crea el archivo de prueba. Usted acaba de crear el archivo de prueba allí mismo.
Una gran ventaja de esto es que significa que los otros empleados (no usted porque nunca haría esto) tendrán menos probabilidades de evitar escribir código de prueba para empezar porque es demasiado trabajo. Incluso cuando agregan métodos a las clases existentes, es menos probable que no sientan la necesidad de agregar pruebas al código de prueba existente.
Una desventaja es que esto dificulta la liberación de su código de producción sin las pruebas que lo acompañan. Aunque si usa convenciones de nomenclatura estrictas, aún podría ser posible. Por ejemplo, he estado usando ClassName.php, ClassNameUnitTest.php y ClassNameIntegrationTest.php. Cuando quiero ejecutar todas las pruebas unitarias, hay un conjunto que busca los archivos que terminan en UnitTest.php. El paquete de prueba de integración funciona de manera similar. Si quisiera, podría usar una técnica similar para evitar que las pruebas sean lanzadas a la producción.
Otra desventaja de este enfoque es cuando solo buscas código real, no código de prueba, requiere un poco más de esfuerzo diferenciar entre los dos.
Una clase de prueba por clase:
Esto está lejos de ser experimental para la mayoría de los programadores, pero es para mí. Estoy experimentando con solo haber probado una clase de prueba por clase. En el pasado tenía un directorio completo para cada clase que se estaba probando y luego tenía varias clases dentro de ese directorio. Cada clase de prueba configuró la clase que se estaba probando de cierta manera, y luego tuvo un montón de métodos cada uno con una afirmación diferente. Pero luego comencé a darme cuenta de que ciertas condiciones en las que obtendría estos objetos tenían cosas en común con otras condiciones que entraba en otras clases de prueba. La duplicación se volvió demasiado difícil de manejar, así que comencé a crear abstracciones para hacerlo remotamente. El código de prueba se volvió muy difícil de entender y mantener. Me di cuenta de esto, pero no pude ver una alternativa que tuviera sentido para mí. Con solo tener una clase de prueba por clase parecía que no sería capaz de probar casi las situaciones suficientes sin ser abrumador para tener todo ese código de prueba dentro de una clase de prueba. Ahora tengo una perspectiva diferente sobre eso. Incluso si tenía razón, esto es un gran obstáculo para otros programadores y para mí, que quiero escribir y mantener las pruebas. Ahora estoy experimentando con forzarme a tener una clase de prueba por clase que se prueba. Si encuentro demasiadas cosas para probar en esa clase de prueba, estoy experimentando con ver esto como una indicación de que la clase que se está probando está haciendo demasiado, y se debe dividir en varias clases. Para eliminar la duplicación, intento concentrarme tanto en las abstracciones más simples que permitan que todo exista en una clase de prueba legible.