Hay dos razones para escribir pruebas:
- Afirmar el comportamiento esperado
- Prevenir la regresión del comportamiento
La toma de (1) Afirmar el comportamiento esperado:
Cuando afirma el comportamiento esperado, quiere asegurarse de que el código funcione como cree que debería. Esta es efectivamente una forma automatizada de realizar su verificación manual de rutina que cualquier desarrollador realizaría al implementar cualquier tipo de código:
- ¿Lo que acabo de escribir funciona?
- ¿Este ciclo realmente termina?
- ¿Se repite en el orden que creo que está?
- ¿Funcionaría esto para una entrada nula?
Esas son preguntas que todos respondemos en nuestras cabezas y, normalmente, intentamos ejecutar el código en nuestras cabezas también, asegurarnos de que parezca que funciona. Para estos casos, a menudo es útil que la computadora los responda de manera definitiva. Entonces escribimos una prueba unitaria que afirma que sí. Esto nos da confianza en nuestro código, nos ayuda a encontrar defectos temprano e incluso puede ayudarnos a implementar el código.
Es una buena idea hacer esto donde lo crea necesario. Cualquier código que sea un poco complicado de entender o que no sea trivial. Incluso el código trivial podría beneficiarse de ello. Se trata de tu propia confianza. La frecuencia con que lo haga y hasta dónde llegar dependerá de su propia satisfacción. Deténgase cuando pueda responder con seguridad Sí a: ¿Está seguro de que esto funciona?
Para este tipo de pruebas, a usted no le importa la visibilidad, las interfaces ni nada de eso, solo le importa tener un código que funcione. Entonces, sí, probaría los métodos privados y protegidos si cree que deben probarse para responder Sí a la pregunta.
La toma de (2) Prevención de la regresión del comportamiento:
Una vez que tenga el código que funcione, debe tener un mecanismo para proteger este código de daños futuros. Si nadie volviera a tocar su fuente y su configuración nunca más, no necesitaría esto, pero en la mayoría de los casos, usted u otros estarán tocando la fuente y las configuraciones de su software. Es muy probable que esta manipulación interna rompa su código de trabajo.
Ya existen mecanismos en la mayoría de los idiomas como una forma de protegerse contra este daño. Las características de visibilidad son un mecanismo. Un método privado está aislado y oculto. La encapsulación es otro mecanismo, en el que se compartimentan las cosas, para que cambiar otros compartimentos no afecte a los demás.
El mecanismo general para esto se llama: codificación hasta el límite. Al crear límites entre partes del código, protege todo lo que se encuentra dentro de un límite de las cosas que están fuera de él. Los límites se convierten en el punto de interacción y el contrato mediante el cual las cosas interactúan.
Esto significa que los cambios en un límite, ya sea rompiendo su interfaz o rompiendo su comportamiento esperado, dañarían y posiblemente romperían otros límites que dependían de él. Por eso es una buena idea tener una prueba unitaria, que se dirija a esos límites y afirme que no cambian en semántica ni en comportamiento.
Esta es su prueba unitaria típica, de la que casi todo el mundo habla cuando menciona TDD o BDD. El punto es endurecer los límites y protegerlos del cambio. No desea probar métodos privados para esto, porque un método privado no es un límite. Los métodos protegidos son un límite restringido y yo los protegería. No están expuestos al mundo, pero aún están expuestos a otros compartimentos o "Unidades".
¿Qué hacer con esto?
Como hemos visto, hay una buena razón para realizar pruebas unitarias de métodos públicos y protegidos, para afirmar que nuestras interfaces no cambian. Y también hay una buena razón para probar métodos privados, como para afirmar que nuestra implementación funciona. Entonces, ¿deberíamos probarlos todos por unidad?
Si y no.
En primer lugar : pruebe todos los métodos que crea que necesita una prueba definitiva de que funciona en la mayoría de los casos para poder estar seguro de que su código funciona, sin importar la visibilidad. Luego, desactive esas pruebas. Han hecho su trabajo.
Finalmente : escribe pruebas para tus límites. Realice una prueba unitaria para cada punto que utilizarán otras unidades de su sistema. Asegúrese de que esta prueba afirme el contrato semántico, el nombre del método, el número de argumentos, etc. Y también asegúrese de que la prueba afirme el comportamiento disponible de la unidad. Su prueba debe demostrar cómo usar la unidad y qué puede hacer la unidad. Mantenga estas pruebas habilitadas para que se ejecuten en cada inserción de código.
NOTA: La razón por la que desactivó el primer conjunto de pruebas es para permitir que se produzca el trabajo de refactorización. Una prueba activa es un código de acoplamiento. Evita modificaciones futuras del código que está probando. Solo desea esto para sus interfaces y contratos de interacción.