Al investigar las mejores prácticas de pruebas unitarias para ayudar a elaborar pautas para mi organización, me he preguntado si es mejor o útil separar los accesorios de prueba (clases de prueba) o mantener todas las pruebas para una sola clase en un archivo.
Fwiw, me refiero a las "pruebas unitarias" en el sentido puro de que son pruebas de caja blanca dirigidas a una sola clase, una afirmación por prueba, todas las dependencias burladas, etc.
Un escenario de ejemplo es una clase (llámela Documento) que tiene dos métodos: CheckIn y CheckOut. Cada método implementa varias reglas, etc. que controlan su comportamiento. Siguiendo la regla de una afirmación por prueba, tendré varias pruebas para cada método. Puedo colocar todas las pruebas en una sola DocumentTests
clase con nombres como CheckInShouldThrowExceptionWhenUserIsUnauthorized
y CheckOutShouldThrowExceptionWhenUserIsUnauthorized
.
O podría tener dos clases de prueba separadas: CheckInShould
y CheckOutShould
. En este caso, mis nombres de prueba se acortarían, pero se organizarían para que todas las pruebas de un comportamiento (método) específico estén juntas.
Estoy seguro de que hay ventajas y desventajas para abordar y me pregunto si alguien ha seguido la ruta con varios archivos y, de ser así, ¿por qué? O, si ha optado por el enfoque de archivo único, ¿por qué cree que es mejor?
fuente
testResponseContainsSuccessTrue()
,testResponseContainsMyData()
ytestResponseStatusCodeIsOk()
. Como quieren que ellos en una solatestResponse()
que tiene tres afirma:assertEquals(200, response.status)
,assertEquals({"data": "mydata"}, response.data)
yassertEquals(true, response.success)
Respuestas:
Es raro, pero a veces tiene sentido tener múltiples clases de prueba para una clase determinada bajo prueba. Por lo general, haría esto cuando se requieran diferentes configuraciones y se compartan en un subconjunto de las pruebas.
fuente
Realmente no puedo ver ninguna razón convincente por la que dividiría una prueba para una sola clase en varias clases de prueba. Dado que la idea de conducir debe ser mantener la cohesión a nivel de clase, también debes esforzarte por lograrlo a nivel de prueba. Solo algunas razones aleatorias:
fuente
Si se ve obligado a dividir las pruebas unitarias para una clase en varios archivos, eso puede ser una indicación de que la clase en sí está mal diseñada. No puedo pensar en ningún escenario en el que sería más beneficioso dividir las pruebas unitarias para una clase que se adhiera razonablemente al Principio de Responsabilidad Única y otras mejores prácticas de programación.
Además, tener nombres de métodos más largos en las pruebas unitarias es aceptable, pero si le molesta, siempre puede repensar la convención de nomenclatura de la prueba unitaria para acortar los nombres.
fuente
Uno de mis argumentos en contra de separar las pruebas en varias clases es que se hace más difícil para otros desarrolladores en el equipo (especialmente aquellos que no son expertos en pruebas) localizar las pruebas existentes ( Gee, me pregunto si ya hay una prueba para esto ¿Me pregunto dónde estaría? ) y también dónde poner nuevas pruebas ( voy a escribir una prueba para este método en esta clase, pero no estoy muy seguro de si debo ponerlas en un archivo nuevo o en uno existente uno? )
En el caso de que diferentes pruebas requieran configuraciones drásticamente diferentes, he visto a algunos practicantes de TDD colocar el "accesorio" o la "configuración" en diferentes clases / archivos, pero no las pruebas en sí.
fuente
Desearía poder recordar / encontrar el enlace donde se demostró por primera vez la técnica que he elegido adoptar. Esencialmente creo una sola clase abstracta para cada clase bajo prueba que contiene accesorios de prueba anidados (clases) para cada miembro que se está probando. Esto proporciona la separación originalmente deseada pero mantiene todas las pruebas en el mismo archivo para cada ubicación. Además, esto genera un nombre de clase que permite agrupar y ordenar fácilmente en el corredor de prueba.
Aquí hay un ejemplo de cómo se aplica este enfoque a mi escenario original:
Esto resulta en las siguientes pruebas que aparecen en la lista de pruebas:
A medida que se agregan más pruebas, se agrupan y clasifican fácilmente por nombre de clase, lo que también mantiene todas las pruebas para la clase bajo prueba listadas juntas.
Otra ventaja de este enfoque que he aprendido a aprovechar es la naturaleza orientada a objetos de esta estructura, lo que significa que puedo definir el código dentro de los métodos de inicio y limpieza de la clase base DocumentTests que serán compartidos por todas las clases anidadas, así como dentro de cada clase anidada para las pruebas que contiene.
fuente
Intenta pensar al revés por un minuto.
¿Por qué tendrías solo una clase de prueba por clase? ¿Estás haciendo una prueba de clase o una prueba de unidad? ¿Incluso depende de las clases ?
Se supone que una prueba unitaria está probando un comportamiento específico, en un contexto específico. El hecho de que su clase ya tenga un
CheckIn
medio significa que debe haber un comportamiento que lo necesite en primer lugar.¿Cómo pensarías sobre este pseudocódigo?
Ahora no estás probando directamente el
checkIn
método. En su lugar, está probando un comportamiento (que es afortunadamente;)), y si necesita refactorizarloDocument
y dividirlo en diferentes clases, o fusionar otra clase en él, sus archivos de prueba siguen siendo coherentes, todavía tienen sentido ya que nunca estás cambiando la lógica al refactorizar, solo la estructura.Un archivo de prueba para una clase simplemente hace que sea más difícil refactorizar cada vez que lo necesite, y las pruebas también tienen menos sentido en términos de dominio / lógica del código en sí.
fuente
En general, recomendaría pensar en las pruebas como pruebas de un comportamiento de la clase en lugar de un método. Es decir, algunas de sus pruebas pueden necesitar llamar a ambos métodos en la clase para evaluar el comportamiento esperado.
Por lo general, comienzo con una clase de prueba unitaria por clase de producción, pero eventualmente puedo dividir esa clase de prueba unitaria en varias clases de prueba según el comportamiento que prueban. En otras palabras, recomendaría no dividir la clase de prueba en CheckInShould y CheckOutShould, sino dividir por el comportamiento de la unidad bajo prueba.
fuente
1. Considere qué es probable que salga mal
Durante el desarrollo inicial, usted sabe bastante bien lo que está haciendo y ambas soluciones probablemente funcionarán bien.
Se vuelve más interesante cuando una prueba falla después de un cambio mucho más tarde. Hay dos posibilidades:
CheckIn
(oCheckOut
). Nuevamente, tanto la solución de un solo archivo como la de dos archivos están bien en ese caso.CheckIn
yCheckOut
(y quizás sus pruebas) de una manera que tenga sentido para cada uno de ellos, pero no para los dos juntos. Has roto la coherencia de la pareja. En ese caso, dividir las pruebas en dos archivos dificultará la comprensión del problema.2. Considere para qué pruebas se utilizan
Las pruebas tienen dos propósitos principales:
¿Entonces?
Ambas perspectivas sugieren que mantener las pruebas juntas puede ayudar, pero no perjudicará.
fuente