unit-testing - que - tdd ejemplo
¿Deberían los métodos privados/protegidos estar bajo prueba unitaria? (12)
¡No! Solo interfaces de prueba.
Uno de los grandes beneficios de TDD es asegurar que la interfaz funcione sin importar cómo haya elegido implementar los métodos privados.
En el desarrollo TDD, lo primero que suele hacer es crear su interfaz y luego comenzar a escribir sus pruebas unitarias contra esa interfaz. A medida que avances en el proceso TDD, terminarás creando una clase que implementará la interfaz y luego, en algún momento, pasará la prueba de tu unidad.
Ahora mi pregunta es acerca de los métodos privados y protegidos que podría tener que escribir en mi clase en apoyo de los métodos / propiedades expuestos por la interfaz:
¿Deben los métodos privados en la clase tener sus propias pruebas unitarias?
¿Deben los métodos protegidos en la clase tener sus propias pruebas unitarias?
Mis pensamientos:
Especialmente porque estoy codificando interfaces, no debería preocuparme por los métodos protegidos / privados ya que son cuadros negros.
Debido a que estoy usando interfaces, estoy escribiendo pruebas unitarias para validar que el contrato definido está implementado correctamente por las diferentes clases que implementan la interfaz, así que nuevamente no debería preocuparme por los métodos privados / protegidos y deben ejercitarse a través de pruebas unitarias que llaman al métodos / propiedades definidos por la interfaz.
Si mi cobertura de código no muestra que los métodos protegidos / privados están siendo afectados, entonces no tengo las pruebas de unidad correctas o tengo un código que no se usa y debe eliminarse.
Completando lo que otros dijeron anteriormente, diría que los métodos protegidos son parte de una interfaz de algún tipo: simplemente es la expuesta a la herencia en lugar de la composición, que es lo que todos piensan al considerar las interfaces.
Marcar un método como protegido en lugar de privado significa que se espera que lo utilice un código de terceros, por lo que debe definirse y probarse algún tipo de contrato, como sucede con las interfaces normales definidas por métodos públicos, que están abiertas tanto para la herencia como para la composición .
Cuando escribe las pruebas unitarias para su clase, no debería preocuparse si la funcionalidad de la clase se implementa directamente o no en el método en la interfaz pública o si se implementa en una serie de métodos privados. Así que sí, debería probar sus métodos privados, pero no debería necesitar llamarlos directamente desde su código de prueba para hacerlo (la prueba directa de los métodos privados vincula estrechamente su implementación con sus pruebas y hace que la refactorización sea innecesariamente difícil).
Los métodos protegidos forman un contrato diferente entre su clase y sus futuros hijos, por lo que debería probarlo de forma similar a su interfaz pública para asegurarse de que el contrato esté bien definido y ejercido.
Estoy de acuerdo con todos los demás: la respuesta a su pregunta es ''no''.
De hecho, tiene toda la razón con su enfoque y sus pensamientos, especialmente sobre la cobertura del código.
También agregaría que la pregunta (y la respuesta "no") también se aplica a los métodos públicos que podría presentar a las clases.
- Si agrega métodos (públicos / protegidos o privados) porque hacen un pase de prueba fallido, entonces habrá logrado más o menos el objetivo de TDD.
- Si agrega métodos (públicos / protegidos o privados) porque decide hacerlo, violando el TDD, entonces su cobertura del código debería detectarlos y debería poder mejorar su proceso.
Además, para C ++ (y debería pensar solo para C ++), implemento interfaces utilizando solo métodos privados, para indicar que la clase solo debe usarse a través de la interfaz que implementa. Me impide llamar erróneamente a mis pruebas nuevos métodos añadidos a mi implementación
Hay dos razones para escribir pruebas:
- Afirmando el comportamiento esperado
- Previniendo la regresión del comportamiento
La toma en (1) Afirmando el comportamiento esperado:
Cuando afirma el comportamiento esperado, quiere asegurarse de que el código funcione como cree que debería. Esta es, de hecho, una forma automatizada de realizar la verificación manual de rutina que cualquier desarrollador realizaría al implementar cualquier tipo de código:
- ¿Funcionó lo que acabo de escribir?
- ¿Este ciclo realmente termina?
- ¿Está girando en el orden que creo que es?
- ¿Esto funcionaría para una entrada nula?
Esas son preguntas que todos respondemos en nuestras cabezas, y normalmente, intentaríamos ejecutar también el código en nuestras cabezas, asegúrese de que parezca que funciona. En 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 de manera temprana e incluso puede ayudarnos a implementar el código.
Es una buena idea hacer esto donde sea que lo considere necesario. Cualquier código que sea un poco difícil de entender, o que no sea trivial. Incluso un código trivial podría beneficiarse de ello. Se trata de tu propia confianza. Con qué frecuencia hacerlo y qué tan lejos llegar dependerá de su propia satisfacción. Deténgase cuando pueda responder con confianza Sí a: ¿Está seguro de que esto funciona?
Para este tipo de prueba, no le importa la visibilidad, las interfaces, o algo así, solo le interesa tener un código que funcione. Por lo tanto, sí, probaría métodos privados y protegidos si considerara que debían someterse a una prueba para que usted respondiera Sí a la pregunta.
La aceptación (2) Prevención de la regresión del comportamiento:
Una vez que tenga el código de trabajo, necesita tener un mecanismo en su lugar para proteger este código del daño futuro. Si nadie volviera a tocar nunca más tu fuente y tu configuración, no necesitarías esto, pero en la mayoría de los casos, tú u otros tocarán la fuente y las configuraciones de tu software. Es muy probable que este violín interno rompa tu código de trabajo.
Los mecanismos existen 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 compartimenta las cosas, de modo que cambiar otros compartimentos no afecta a los demás.
El mecanismo general para esto se llama: codificación a límite. Al crear límites entre partes del código, protege todo 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 por el que 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. Es por eso que es una buena idea tener una prueba unitaria, que se dirija a esos límites y afirme que no cambian en la semántica y en el comportamiento.
Esta es la prueba de unidad típica, de la que todos hablan al mencionar TDD o BDD. El objetivo 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 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 probar los métodos públicos y protegidos de manera individual, como para afirmar que nuestras interfaces no cambian. Y también hay buenas razones para probar métodos privados, como para afirmar que nuestra implementación funciona. Entonces, ¿deberíamos probarlos todos juntos?
Si y no.
En primer lugar : pruebe todos los métodos que considere 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, deshabilite esas pruebas. Han hecho un trabajo allí.
Por último : escribe pruebas para tus límites. Haga una prueba unitaria para cada punto que usarán otras unidades de su sistema. Asegúrese de que esta prueba confirme el contrato semántico, el nombre del método, el número de argumentos, etc. Y también asegúrese de que la prueba confirme 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: El motivo por el que deshabilitó el primer conjunto de pruebas es para permitir que se realice el trabajo de refactorización. Una prueba activa es un acoplamiento de código. Evita la modificación futura del código que está probando. Solo quiere esto para sus interfaces y contratos de interacción.
No estoy de acuerdo con la mayoría de los carteles.
La regla más importante es: CÓDIGO DE TRABAJO TROMPAS REGLAS TEÓRICAS sobre público / protegido / privado.
Su código debe ser probado minuciosamente. Si puede llegar allí escribiendo pruebas para los métodos públicos, que ejercen suficientemente los métodos protegidos / privados, eso es genial.
Si no puede, refactorice para poder hacerlo, o doble las reglas protegidas / privadas.
Hay una gran historia sobre un psicólogo que hizo una prueba a los niños. Le dio a cada niño dos tablas de madera con una cuerda unida a cada extremo, y les pidió que cruzaran una habitación sin tocar sus pies al piso, lo más rápido posible. Todos los niños usaron las tablas como pequeños esquís, un pie en cada tabla, sosteniéndolos por las cuerdas y deslizándose por el piso. Luego les dio la misma tarea, pero usando solo UNA tabla. Giraron / "caminaron" por el suelo, con un pie en cada extremo de la placa única, ¡y fueron MÁS RÁPIDOS!
El hecho de que Java (o cualquier idioma) tenga una función (privada / protegida / pública) no significa necesariamente que esté escribiendo un código mejor porque lo usa.
Ahora, siempre habrá formas de optimizar / minimizar este conflicto. En la mayoría de los idiomas, puede proteger un método (en lugar de public) y colocar la clase de prueba en el mismo paquete (o lo que sea), y el método estará disponible para la prueba. Hay anotaciones que pueden ayudar, según lo descrito por otros carteles. Puedes usar reflection para obtener los métodos privados (yuck).
El contexto también importa. Si está escribiendo una API para uso de personas externas, público / privado es más importante. Si se trata de un proyecto interno, ¿a quién le importa realmente?
Pero al final del día, piense en cuántos errores ha causado la falta de pruebas. Luego, compare la cantidad de errores causados por métodos "demasiado visibles". Esa respuesta debería impulsar tu decisión.
No, no deberías probar métodos privados (cómo lo harías de todos modos sin usar algo horrible como la reflexión). Con los métodos protegidos es un poco menos obvio que en C # puede proteger las cosas internas y creo que está bien hacer eso para probar las clases derivadas que implementan todas sus funcionalidades a través de métodos de patrones de plantilla.
Pero, en general, si crees que tus métodos públicos están haciendo demasiado, entonces es hora de refaccionar tus clases en más clases atómicas y luego probar esas clases.
Si busca una alta cobertura de código (sugiero que lo haga), debe probar todos sus métodos, independientemente de que sean privados o estén protegidos.
Protected es un tipo de punto de discusión diferente, pero en resumen, no debería estar allí en absoluto. O rompe la encapsulación en el código implementado, o te obliga a heredar de esa clase, solo para probarlo en una unidad, incluso algunas veces no necesitas heredar.
Solo ocultar un método al cliente (hacer que sea privado) no le da el privilegio de no ser auditado. Por lo tanto, pueden ser probados por métodos públicos como se mencionó anteriormente.
Tu escribiste:
En el desarrollo TDD, lo primero que suele hacer es crear su interfaz y luego comenzar a escribir sus pruebas unitarias contra esa interfaz. A medida que avances en el proceso TDD, terminarás creando una clase que implementará la interfaz y luego, en algún momento, pasará la prueba de tu unidad.
Permítanme reformular esto en lenguaje BDD :
Al describir por qué una clase es valiosa y cómo se comporta, lo primero que suele hacer es crear un ejemplo de cómo usar la clase, a menudo a través de su interfaz *. A medida que agrega el comportamiento deseado, termina creando una clase que proporciona ese valor, y luego, en algún momento, su ejemplo funciona.
* Puede ser una
Interface
real o simplemente la API accesible de la clase, por ejemplo: Ruby no tiene interfaces.
Esta es la razón por la que no prueba métodos privados, porque una prueba es un ejemplo de cómo usar la clase y no puede usarlos. Algo que puede hacer si lo desea es delegar las responsabilidades en los métodos privados a una clase colaboradora, luego simular / resguardar ese ayudante.
Con los métodos protegidos, estás diciendo que una clase que amplíe tu clase debería tener un comportamiento particular y proporcionar algún valor. Luego podría usar extensiones de su clase para demostrar ese comportamiento. Por ejemplo, si estaba escribiendo una clase de colección ordenada, es posible que desee demostrar que dos extensiones con los mismos contenidos demostraron igualdad.
¡Espero que esto ayude!
Un buen diseño significa dividir la aplicación en múltiples unidades comprobables. Después de hacer esto, algunas unidades están expuestas a la API pública, pero otras pueden no estarlo. Además, los puntos de interacción entre las unidades expuestas y estas unidades "internas" tampoco forman parte de la API pública.
Creo que una vez que tengamos la unidad identificable, se beneficiaría de las pruebas unitarias, independientemente de si están expuestas a través de la API pública o no.
Yo también estoy de acuerdo con la respuesta de @ kwbeam sobre no probar métodos privados. Sin embargo, un punto importante que me gustaría destacar es que los métodos protegidos SON parte de la API exportada de una clase y, por lo tanto, DEBEN probarse.
Es posible que los métodos protegidos no sean de acceso público, pero definitivamente está proporcionando una forma para que las subclases los utilicen o anulen. Algo fuera de la clase puede acceder a ellos y, por lo tanto, debe asegurarse de que esos miembros protegidos se comporten de la manera esperada. Por lo tanto, no pruebe métodos privados, pero pruebe métodos públicos y protegidos.
Si crees que tienes un método privado que contiene una lógica crítica, trataría de extraerlo en un objeto separado, aislarlo y proporcionar una forma de probar su comportamiento.
¡Espero eso ayude!
No, no pienso en probar métodos privados o protegidos. Los métodos privados y protegidos de una clase no son parte de la interfaz pública, por lo que no exponen el comportamiento público. En general, estos métodos se crean mediante refactorizaciones que aplica después de que la prueba se vuelva verde.
Entonces, estos métodos privados son probados implícitamente por las pruebas que afirman el comportamiento de su interfaz pública.
En una nota más filosófica, recuerda que estás probando el comportamiento, no los métodos. Entonces, si piensa en el conjunto de cosas que la clase bajo prueba puede hacer, siempre y cuando pueda probar y afirmar que la clase se comporta como se espera, si hay métodos privados (y protegidos) que la clase utiliza internamente para implementar ese comportamiento es irrelevante. Esos métodos son detalles de implementación del comportamiento público.