unitarias unit test run que pruebas make example php unit-testing phpunit

test - pruebas unitarias php



Organizando Pruebas PHPUnit en Espacios de Nombres (3)

Hay una tercera opción que uso y que encaja perfectamente con la carga automática del compositor: inserte un espacio de nombres de Test después del primer paso en la jerarquía. En su caso, ese espacio de nombres sería /SomeFramework/Tests/Utilities/ y su clase sería /SomeFramework/Tests/Utilities/AwesomeClassTest .

Luego puede poner las pruebas junto con las otras clases en el /SomeFramework/Test , o /SomeFramework/Test en un directorio diferente. Su información de carga automática para composer.json podría tener este aspecto:

{ "autoload": { "psr-0": { "SomeFramework//": "src/", } }, "autoload-dev": { "psr-0": { "SomeFramework//Tests//": "tests/" } } }

Las ventajas del tercer enfoque son:

  • Separación de pruebas y código de producción.
  • Jerarquías de carpetas similares para pruebas y clases de producción.
  • Fácil carga automática

Veo dos opciones para organizar las pruebas unitarias de PHPUnit en una jerarquía de espacio de nombres. ¿Cuáles son las ventajas / desventajas de estos dos enfoques? ¿Hay algún defecto obvio que no haya considerado que haría que una sea la mejor opción?

Considere una clase de muestra como /SomeFramework/Utilities/AwesomeClass :

  • Enfoque 1: coloque cada clase de TestCase en el mismo espacio de nombres que la clase cubierta.

    /SomeFramework/Utilities/AwesomeClassTest

    • Ventajas
      • De acuerdo con el enfoque tradicional para escribir pruebas de PHPUnit.
    • Desventajas
      • Menos flexibilidad.
      • Parece romper el principio detrás del uso de espacios de nombres: las pruebas no relacionadas se agrupan en el mismo espacio de nombres.

  • Enfoque 2: coloque cada TestCase en un espacio de nombres que lleve el nombre de la clase cubierta.

    /SomeFramework/Utilities/AwesomeClass/Test

    • Ventajas
      • Proporciona una forma muy fácil / obvia de agrupar varias clases relacionadas de TestCase, por ejemplo, para diferentes suites de prueba.
    • Desventajas
      • Podría resultar en una jerarquía más profunda y compleja.

Mi solución propuesta y el razonamiento detrás de ella:

Diseño de la carpeta:

. ├── src │   ├── bar │   │   └── BarAwesomeClass.php │   └── foo │   └── FooAwesomeClass.php └── tests ├── helpers │   └── ProjectBaseTestClassWithHelperMethods.php ├── integration │   ├── BarModuleTest.php │   └── FooModuleTest.php └── unit ├── bar │   └── BarAwesomeClassTest.php └── foo └── FooAwesomeClassTest.php

La carpeta helpers/ contiene clases que no son pruebas pero solo se usan en un contexto de prueba. Por lo general, esa carpeta contiene una BaseTestClass que puede contener métodos de ayuda específicos del proyecto y un par de clases de código auxiliar fáciles de reutilizar para que no necesite tantos simulacros.

La integration/ carpeta contiene pruebas que abarcan más clases y prueban partes "más grandes" del sistema. No tiene tantos, pero no existe una asignación 1: 1 a las clases de producción.

La unit/ carpeta asigna 1: 1 a la src/ . Entonces, para cada clase de producción hay una clase que contiene todas las pruebas unitarias para esa clase.

Espacios de nombres

Enfoque 1: coloque cada clase de TestCase en el mismo espacio de nombres que la clase cubierta.

Este enfoque de carpeta debería resolver una de sus desventajas con el Enfoque 1 . Aún tiene la flexibilidad de tener más pruebas de las que una asignación de 1: 1 pura podría darle, pero todo está ordenado y en su lugar.

Parece romper el principio detrás del uso de espacios de nombres: las pruebas no relacionadas se agrupan en el mismo espacio de nombres.

Si las pruebas se sienten "no relacionadas", ¿tal vez el código de producción tenga el mismo problema?

Es cierto que las pruebas no dependen unas de otras, pero podrían usar sus clases "cerradas" como simulacros o usar las reales en caso de DTO u objetos de valor. Así que diría que hay una conexión.

Enfoque 2: coloque cada TestCase en un espacio de nombres que lleve el nombre de la clase cubierta.

Hay un par de proyectos que hacen eso, pero generalmente lo estructuran de manera un poco diferente:

No es /SomeFramework/Utilities/AwesomeClass/Test , sino /SomeFramework/Tests/Utilities/AwesomeClassTest y aún conservan la asignación 1: 1, pero con el espacio de nombres de prueba adicional agregado.

Espacio de nombres de prueba extra

Mi opinión personal es que no me gusta tener espacios de nombres de prueba separados e intentaré encontrar un par de argumentos a favor y en contra de esa elección:

Las pruebas deben servir como documentación sobre cómo usar una clase

Cuando la clase real está en otro espacio de nombres, las pruebas muestran cómo usar esa clase fuera de su propio módulo.

Cuando la clase real está en el mismo espacio de nombres, las pruebas muestran cómo usar esa clase desde dentro de ese módulo.

Las diferencias son bastante menores (generalmente un par de declaraciones de "uso" o rutas de acceso completas)

Cuando tengamos la posibilidad de decir $this->getMock(AwesomeClass::CLASS) en PHP 5.5 en lugar de $this->getMock(''/SomeFramework/Utilities/AwesomeClass'') cada simulacro requerirá una declaración de uso.

Para mí, el uso dentro del módulo es más valioso para la mayoría de las clases

Contaminando el espacio de nombres de "producción"

Cuando dices new /SomeFramework/Utilities/A la finalización automática puede mostrarte AwesomeClass y AwesomeClassTest y algunas personas no quieren eso. Para uso externo, o cuando se envía su fuente, no es un problema, por supuesto, ya que las pruebas no se envían, pero podría ser algo a considerar.


Prefiero el primer enfoque para mantener la coherencia, con la práctica PHPUnit y nuestros otros proyectos. Además, creo solo un caso de prueba por clase bajo prueba. Poner cada uno en su propio espacio de nombres parece excesivo. Como dijo KingCrunch, las pruebas están relacionadas porque las clases que evalúan están relacionadas.

De vez en cuando, un caso de prueba requiere archivos de soporte, como accesorios, pero estos se organizan fácilmente en un subdirectorio / espacio de nombres con el nombre de la clase y a menudo se comparten entre varios casos de prueba.

Una gran desventaja del segundo método es que el nombre de cada caso de Test es Test que tendrá varias ramificaciones:

  • Múltiples ventanas de prueba abiertas tendrán el mismo nombre.
  • La función "Tipo abierto por nombre" de su IDE (CTRL-O en NetBeans) será inútil para las pruebas.
  • El acceso directo de la tecla "ir a prueba" de su IDE (CTRL-SHIFT-T en NetBeans) también puede fallar.