TDD Red-Green-Refactor y si / cómo probar métodos que se vuelven privados

91

Por lo que yo entiendo, la mayoría de la gente parece estar de acuerdo en que los métodos privados no deben probarse directamente, sino a través de los métodos públicos que los llamen. Puedo ver su punto, pero tengo algunos problemas con esto cuando trato de seguir las "Tres leyes de TDD" y utilizo el ciclo "Rojo - verde - refactorizador". Creo que se explica mejor con un ejemplo:

En este momento, necesito un programa que pueda leer un archivo (que contiene datos separados por tabulaciones) y filtrar todas las columnas que contienen datos no numéricos. Supongo que probablemente ya hay algunas herramientas simples disponibles para hacer esto, pero decidí implementarlo desde cero, principalmente porque pensé que podría ser un proyecto agradable y limpio para mí practicar un poco con TDD.

Entonces, primero, "me puse el sombrero rojo", es decir, necesito una prueba que falla. Pensé, necesitaré un método que encuentre todos los campos no numéricos en una línea. Entonces escribo una prueba simple, por supuesto, no se compila de inmediato, así que comienzo a escribir la función en sí, y después de un par de ciclos de ida y vuelta (rojo / verde) tengo una función de trabajo y una prueba completa.

A continuación, continúo con una función, "collectNonNumericColumns" que lee el archivo, una línea a la vez, y llama a mi función "findNonNumericFields" en cada línea para reunir todas las columnas que eventualmente deben eliminarse. Un par de ciclos rojo-verde, y he terminado, una vez más, tengo una función de trabajo y una prueba completa.

Ahora, me imagino que debería refactorizar. Dado que mi método "findNonNumericFields" fue diseñado solo porque pensé que lo necesitaría al implementar "collectNonNumericColumns", me parece razonable permitir que "findNonNumericFields" se vuelva privado. Sin embargo, eso rompería mis primeras pruebas, ya que ya no tendrían acceso al método que estaban probando.

Entonces, termino con métodos privados y un conjunto de pruebas que lo prueban. Dado que tanta gente aconseja que no se prueben los métodos privados, parece que me he arrinconado aquí. ¿Pero dónde fracasé exactamente?

Supongo que podría haber comenzado en un nivel superior, escribiendo una prueba que prueba lo que eventualmente se convertirá en mi método público (es decir, findAndFilterOutAllNonNumericalColumns), pero eso se siente algo contrario al punto completo de TDD (al menos según el tío Bob) : Que debe cambiar constantemente entre escribir pruebas y código de producción, y que en cualquier momento, todas sus pruebas funcionaron en el último minuto más o menos. Porque si empiezo escribiendo una prueba para un método público, habrá varios minutos (u horas, o incluso días en casos muy complejos) antes de obtener todos los detalles en los métodos privados para que la prueba pruebe al público método pasa.

¿Entonces lo que hay que hacer? ¿TDD (con el ciclo rápido de refactorización rojo-verde) simplemente no es compatible con métodos privados? ¿O hay una falla en mi diseño?

Henrik Berg
fuente
2
O estas dos piezas de funcionalidad son lo suficientemente diferentes como para ser unidades diferentes, en cuyo caso los métodos privados probablemente deberían estar en sus propias clases, o son la misma unidad en cuyo caso no veo por qué estás escribiendo pruebas para comportamiento interno de la unidad. Con respecto al penúltimo párrafo, no veo el conflicto. ¿Por qué necesitaría escribir un método privado complejo completo para pasar un caso de prueba? ¿Por qué no expulsarlo gradualmente a través del método público, o comenzar con él en línea y luego extraerlo?
Ben Aaronson
26
¿Por qué la gente toma modismos y clichés de los libros y blogs de programación como pautas reales sobre cómo programar?
AK_
77
No me gusta el TDD exactamente por esta razón: si estás en una nueva área, harás mucho trabajo extra mientras intentas averiguar cómo debería ser la arquitectura y cómo funcionan ciertas cosas. Por otro lado: si estás en un área en la que ya tienes experiencia, será beneficioso escribir primero las pruebas además de molestarte porque intellisense no entiende por qué escribes código no compilable. Soy mucho más fanático de pensar en el diseño, escribirlo y luego probarlo en la unidad.
Jeroen Vannevel
1
"La mayoría de la gente parece estar de acuerdo en que los métodos privados no deben probarse directamente" - no, pruebe un método directamente si tiene sentido hacerlo. Escóndelo como privatesi tuviera sentido hacerlo.
osa

Respuestas:

44

Unidades

Creo que puedo determinar exactamente dónde comenzó el problema:

Pensé, necesitaré un método que encuentre todos los campos no numéricos en una línea.

Esto debe seguirse inmediatamente preguntándose "¿Será una unidad comprobable por separado gatherNonNumericColumnso parte de la misma?"

Si la respuesta es " sí, separar ", entonces su curso de acción es simple: ese método debe ser público en una clase apropiada, para que pueda probarse como una unidad. Su mentalidad es algo así como "Necesito probar un método de prueba y también tengo que probar otro método"

Sin embargo, por lo que dices, pensaste que la respuesta es " no, parte de lo mismo ". En este punto, su plan ya no debería ser escribir y probar completamente y findNonNumericFields luego escribir gatherNonNumericColumns. En cambio, debería ser simplemente escribir gatherNonNumericColumns. Por ahora, findNonNumericFieldsdebería ser una parte probable del destino que tiene en mente cuando elige su próximo caso de prueba roja y realiza su refactorización. Esta vez su mentalidad es "Necesito probar un método, y mientras lo hago, debo tener en cuenta que mi implementación final probablemente incluirá este otro método".


Manteniendo un ciclo corto

Hacer lo anterior no debería conducir a los problemas que describe en su penúltimo párrafo:

Porque si empiezo escribiendo una prueba para un método público, habrá varios minutos (u horas, o incluso días en casos muy complejos) antes de obtener todos los detalles en los métodos privados para que la prueba pruebe al público método pasa.

En ningún momento esta técnica requiere que escriba una prueba roja que solo se volverá verde cuando implemente la totalidad findNonNumericFieldsdesde cero. Es mucho más probable findNonNumericFieldsque comience como un código en línea en el método público que está probando, que se desarrollará en el transcurso de varios ciclos y finalmente se extraerá durante una refactorización.


Mapa vial

Para dar una hoja de ruta aproximada para este ejemplo en particular, no sé los casos de prueba exactos que usó, pero digamos que estaba escribiendo gatherNonNumericColumnscomo su método público. Entonces, lo más probable es que los casos de prueba sean los mismos para los que escribió findNonNumericFields, cada uno usando una tabla con solo una fila. Cuando ese escenario de una fila se implementó por completo, y quería escribir una prueba para obligarlo a extraer el método, escribiría un caso de dos filas que requeriría que agregue su iteración.

Ben Aaronson
fuente
2
Creo que esta es la respuesta aquí mismo. Adoptando TDD en un entorno OOP, a menudo me resulta difícil superar mis propios instintos ascendentes. Sí, las funciones deberían ser pequeñas, pero eso es después de refactorizar. Antes, pueden ser enormes monolitos. +1
João Mendes
2
@ JoãoMendes Bueno, no estoy seguro de que deba llegar al estado de un gran monolito antes de refactorizar, especialmente en ciclos de RGR muy cortos. Pero sí, dentro de una unidad comprobable, trabajar de abajo hacia arriba puede conducir a los problemas que describe el OP.
Ben Aaronson
1
OK, creo que entiendo dónde salió mal ahora. Muchas gracias a todos ustedes (marcó esta como la respuesta, pero la mayoría de las otras respuestas son igualmente útiles también)
Henrik Berg
66

Mucha gente piensa que las pruebas unitarias se basan en métodos; no es. Debe basarse en la unidad más pequeña que tenga sentido. Para la mayoría de las cosas, esto significa que la clase es lo que debería probar como una entidad completa. No métodos individuales en él.

Ahora, obviamente, invocará métodos en la clase, pero debería pensar que las pruebas se aplican al objeto de caja negra que tiene, por lo que debería poder ver las operaciones lógicas que proporciona su clase; Estas son las cosas que necesita probar. Si su clase es tan grande que la operación lógica es demasiado compleja, entonces tiene un problema de diseño que debe solucionarse primero.

Una clase con mil métodos puede parecer comprobable, pero si solo prueba cada método individualmente, realmente no está probando la clase. Algunas clases pueden requerir estar en un cierto estado antes de llamar a un método, por ejemplo, una clase de red que necesita una conexión configurada antes de enviar datos. El método de envío de datos no puede considerarse independientemente de toda la clase.

Entonces debería ver que los métodos privados son irrelevantes para las pruebas. Si no puede ejercer sus métodos privados llamando a la interfaz pública de su clase, entonces esos métodos privados son inútiles y no se utilizarán de todos modos.

Creo que muchas personas intentan convertir los métodos privados en unidades comprobables porque parece fácil ejecutar pruebas para ellos, pero esto lleva la granularidad de la prueba demasiado lejos. Martin Fowler dice

Aunque empiezo con la noción de que la unidad es una clase, a menudo tomo un montón de clases estrechamente relacionadas y las trato como una sola unidad

lo que tiene mucho sentido para un sistema orientado a objetos, los objetos están diseñados para ser unidades. Si desea probar métodos individuales, tal vez debería crear un sistema de procedimiento como C, o una clase compuesta en su totalidad de funciones estáticas.

gbjbaanb
fuente
14
Para mí, parece que esta respuesta ignora por completo el enfoque TDD en la pregunta del OP. Es solo una repetición del mantra "no probar métodos privados", pero no explica cómo TDD, que en realidad está basado en métodos, podría funcionar con un enfoque de prueba de unidad no basado en métodos.
Doc Brown
66
@DocBrown no, le responde completamente al decir "no sobregranice" sus unidades y le dificulte la vida. TDD no está basado en el método, está basado en la unidad donde una unidad es lo que tiene sentido. Si tiene una biblioteca C, entonces sí, cada unidad será una función. Si tienes una clase, la unidad es un objeto. Como dice Fowler, a veces una unidad es varias clases estrechamente relacionadas. Creo que muchas personas consideran que las pruebas unitarias son método por método simplemente porque algunas herramientas tontas generan apéndices basados ​​en métodos.
gbjbaanb
3
@gbjbaanb: intente sugerir una prueba que permita al OP implementar su "recopilación de campos no numéricos en una línea" utilizando TDD puro primero, sin tener la interfaz pública de la clase que pretende escribir ya escrita.
Doc Brown
8
Tengo que estar de acuerdo con @DocBrown aquí. El problema del interrogador no es que desee más granularidad de prueba de la que puede lograr sin probar métodos privados. Es que ha tratado de seguir un estricto enfoque TDD y, sin planearlo como tal, eso lo llevó a golpear una pared donde de repente descubre que tiene un montón de pruebas para lo que debería ser un método privado. Esta respuesta no ayuda con eso. Es una buena respuesta a alguna pregunta, pero no esta.
Ben Aaronson
77
@Matthew: Su error es que él escribió la función en primer lugar. Idealmente, debería haber escrito el método público como código de espagueti y luego refactorizarlo en una función privada en el ciclo de refactorización, no marcarlo como privado en el ciclo de refactorización.
slebetman
51

El hecho de que sus métodos de recopilación de datos sean lo suficientemente complejos como para merecer pruebas y lo suficientemente separados de su objetivo principal como para ser métodos propios en lugar de parte de algunos puntos de bucle a la solución: haga que estos métodos no sean privados, sino miembros de otra clase que proporciona la funcionalidad de recopilación / filtrado / tabulación.

Luego, escribe pruebas para los aspectos tontos de mezcla de datos de la clase auxiliar (por ejemplo, "distinguir números de caracteres") en un lugar, y pruebas para su objetivo principal (por ejemplo, "obtener las cifras de ventas") en otro lugar, y usted no No tiene que repetir las pruebas de filtrado básicas en las pruebas para su lógica comercial normal.

En general, si su clase que hace una cosa contiene un código extenso para hacer otra cosa que se requiere, pero aparte de su propósito principal, ese código debería vivir en otra clase y ser llamado a través de métodos públicos. No debe ocultarse en los rincones privados de una clase que solo contiene accidentalmente ese código. Esto mejora la capacidad de prueba y la comprensión al mismo tiempo.

Kilian Foth
fuente
Sí estoy de acuerdo con usted. Pero tengo un problema con su primera declaración, tanto la parte "suficientemente compleja" como la parte "lo suficientemente separada". Con respecto a "lo suficientemente complejo": estoy tratando de hacer un ciclo rojo-verde rápido, lo que significa que solo puedo codificar como máximo un minuto más o menos a la vez antes de pasar a las pruebas (o al revés). Eso significa que mis pruebas serán muy finas. Pensé que esa era una de las ventajas con TDD, pero tal vez lo he exagerado, por lo que se convierte en una desventaja.
Henrik Berg
Con respecto a "lo suficientemente separado": he aprendido (nuevamente de unclebob) que las funciones deberían ser pequeñas y que deberían ser más pequeñas que eso. Así que básicamente trato de hacer funciones de 3-4 líneas. Por lo tanto, más o menos toda la funcionalidad se separa en métodos propios, sin importar cuán pequeña y simple sea.
Henrik Berg
De todos modos, creo que los aspectos de mezcla de datos (por ejemplo, findNonNumericFields) realmente deberían ser privados. Y si lo separo en otra clase, tendré que hacerlo público de todos modos, así que no veo el punto en eso.
Henrik Berg
66
@HenrikBerg piensa por qué tienes objetos en primer lugar: no son formas convenientes de agrupar funciones, sino unidades independientes que hacen que sea más fácil trabajar con sistemas complejos. Por lo tanto, deberías pensar en probar la clase como una cosa.
gbjbaanb
@gbjbaanb Yo diría que ambos son lo mismo.
RubberDuck
29

Personalmente, creo que llegaste muy lejos en la mentalidad de implementación cuando escribiste las pruebas. Usted asumió que tendría que ciertos métodos. ¿Pero realmente necesita que hagan lo que se supone que debe hacer la clase? ¿Fallaría la clase si alguien viniera y los refactorizara internamente? Si estaba usando la clase (y esa debería ser la mentalidad del evaluador en mi opinión), realmente podría importarle menos si hay un método explícito para verificar los números.

Debe probar la interfaz pública de una clase. La implementación privada es privada por una razón. No es parte de la interfaz pública porque no es necesaria y puede cambiar. Es un detalle de implementación.

Si escribe pruebas en la interfaz pública, nunca obtendrá el problema con el que se encontró. Puede crear casos de prueba para la interfaz pública que cubran sus métodos privados (excelente) o no puede. En ese caso, podría ser el momento de pensar detenidamente sobre los métodos privados y tal vez desecharlos todos juntos si de todos modos no se pueden alcanzar.

nvoigt
fuente
1
Los "detalles de implementación" son cosas como "¿usé una XOR o una variable temporal para intercambiar entradas entre variables". Los métodos protegidos / privados tienen contratos, como cualquier otra cosa. Toman entrada, trabajan con ella y producen algo de salida, bajo ciertas restricciones. Cualquier cosa con un contrato en última instancia debe probarse, no necesariamente para aquellos que consumen su biblioteca, sino para aquellos que la mantienen y la modifican después de usted. El hecho de que no sea "público" no significa que no sea parte de una API.
Knetic
11

No haces TDD en función de lo que esperas que la clase haga internamente.

Sus casos de prueba deben basarse en lo que la clase / funcionalidad / programa tiene que hacer con el mundo externo. En su ejemplo, ¿alguna vez el usuario llamará a su clase de lector parafind all the non-numerical fields in a line?

Si la respuesta es "no", entonces es una mala prueba escribir en primer lugar. Desea escribir la prueba de funcionalidad a nivel de clase / interfaz , no el nivel "qué deberá implementar el método de clase para que esto funcione", que es lo que es su prueba.

El flujo de TDD es:

  • rojo (qué está haciendo la clase / objeto / función / etc. al mundo externo)
  • verde (escriba el código mínimo para que esta función del mundo externo funcione)
  • refactorizar (cuál es el mejor código para hacer que esto funcione)

NO es para hacer "porque necesitaré X en el futuro como método privado, déjenme implementarlo y probarlo primero". Si te encuentras haciendo esto, estás haciendo la etapa "roja" incorrectamente. Este parece ser tu problema aquí.

Si te encuentras escribiendo frecuentemente pruebas para métodos que se convierten en métodos privados, estás haciendo una de las siguientes cosas:

  • No comprende correctamente su interfaz / casos de uso de nivel público lo suficientemente bien como para escribir una prueba para ellos
  • Cambiando drásticamente su diseño y refactorizando varias pruebas (lo que puede ser algo bueno, dependiendo de si esa funcionalidad se prueba en pruebas más recientes)
Enderland
fuente
9

Estás encontrando un error común con las pruebas en general.

La mayoría de las personas que son nuevas en las pruebas comienzan pensando de esta manera:

  • escribir una prueba para la función F
  • implementar F
  • escribir una prueba para la función G
  • implementar G usando una llamada a F
  • escribir una prueba para una función H
  • implementar H usando una llamada a G

y así.

El problema aquí es que, de hecho, no tiene una prueba unitaria para la función H. La prueba que se supone que prueba H realmente prueba H, G y F al mismo tiempo.

Para resolver esto, debe darse cuenta de que las unidades comprobables nunca deben depender unas de otras, sino más bien de sus interfaces . En su caso, donde las unidades son funciones simples, las interfaces son solo su firma de llamada. Por lo tanto, debe implementar G de tal manera que se pueda usar con cualquier función que tenga la misma firma que F.

Cómo exactamente esto se puede hacer depende de su lenguaje de programación. En muchos idiomas, puede pasar funciones (o punteros a ellas) como argumentos a otras funciones. Esto le permitirá probar cada función de forma aislada.

initcrash
fuente
3
Desearía poder votar esto muchas veces más. Simplemente lo resumiría, ya que no ha diseñado su solución correctamente.
Justin Ohms
En un lenguaje como C, esto tiene sentido, pero para los lenguajes OO donde la unidad generalmente debería ser una clase (con métodos públicos y privados), entonces debería probar la clase, no todos los métodos privados de forma aislada. Aislando tus clases, sí. Aislando los métodos en cada clase, no.
gbjbaanb
8

Se supone que las pruebas que usted escribe durante el Desarrollo conducido por pruebas se aseguran de que una clase implemente correctamente su API pública, al tiempo que se asegura de que esa API pública sea fácil de probar y usar.

Por supuesto, puede usar métodos privados para implementar esa API, pero no es necesario crear pruebas a través de TDD: la funcionalidad se probará porque la API pública funcionará correctamente.

Ahora suponga que sus métodos privados son lo suficientemente complicados como para merecer pruebas independientes, pero no tienen sentido como parte de la API pública de su clase original. Bueno, esto probablemente significa que en realidad deberían ser métodos públicos en alguna otra clase, una que su clase original aprovecha en su propia implementación.

Al probar solo la API pública, hace que sea mucho más fácil modificar los detalles de implementación en el futuro. Las pruebas inútiles solo te molestarán más tarde cuando necesiten reescribirse para admitir algunas refactorizaciones elegantes que acabas de descubrir.

Bill Michell
fuente
4

Creo que la respuesta correcta es la conclusión a la que llegó acerca de comenzar con los métodos públicos. Comenzaría escribiendo una prueba que llame a ese método. Fallaría, por lo que crea un método con ese nombre que no hace nada. Entonces, tal vez haga una prueba que verifique el valor de retorno.

(No estoy del todo claro en cuanto a lo que hace su función. ¿Devuelve una cadena con el contenido del archivo con los valores no numéricos eliminados?)

Si su método devuelve una cadena, entonces verifica ese valor de retorno. Así que continúas construyéndolo.

Creo que cualquier cosa que suceda en un método privado debería estar en el método público en algún momento durante su proceso, y luego solo pasar al método privado como parte de un paso de refactorización. Refactorizar no requiere tener pruebas fallidas, que yo sepa. Solo necesita pruebas fallidas al agregar funcionalidad. Solo necesita ejecutar sus pruebas después del refactor para asegurarse de que todas pasen.

Matt Dyer
fuente
3

se siente como si me hubiera pintado en una esquina aquí. ¿Pero dónde fracasé exactamente?

Hay un viejo adagio.

Cuando fallas en planificar, planeas fallar.

La gente parece pensar que cuando TDD, simplemente te sientas, escribes pruebas, y el diseño simplemente ocurrirá mágicamente. Esto no es verdad Necesitas tener un plan de alto nivel. He descubierto que obtengo mis mejores resultados de TDD cuando diseño la interfaz (API pública) primero. Personalmente, creo un actual interfaceque define la clase primero.

jadeo ¡Escribí un "código" antes de escribir cualquier prueba! Bueno no. No lo hice Escribí un contrato a seguir, un diseño . Sospecho que podría obtener resultados similares anotando un diagrama UML en papel cuadriculado. El punto es que debes tener un plan. TDD no es una licencia para ir pirateando un código.

Realmente siento que "Test First" es un nombre inapropiado. Diseño Primero luego prueba.

Por supuesto, siga los consejos que otros han dado sobre extraer más clases de su código. Si siente la necesidad de probar las partes internas de una clase, extraiga esas partes internas en una unidad fácilmente probada e inyéctela.

RubberDuck
fuente
2

¡Recuerde que las pruebas también se pueden refactorizar! Si hace que un método sea privado, está reduciendo la API pública y, por lo tanto, es perfectamente aceptable descartar algunas pruebas correspondientes para esa "funcionalidad perdida" (también conocida como complejidad reducida).

Otros han dicho que su método privado será llamado como parte de sus otras pruebas de API, o será inalcanzable y, por lo tanto, borrable. De hecho, las cosas son más precisas si pensamos en las rutas de ejecución .

Por ejemplo, si tenemos un método público que realiza la división, es posible que queramos probar la ruta que da como resultado la división por cero. Si hacemos que el método sea privado, tenemos una opción: podemos considerar la ruta de división por cero, o podemos eliminar esa ruta considerando cómo se llama por los otros métodos.

De esta manera, podemos descartar algunas pruebas (por ejemplo, dividir por cero) y refactorizar las demás en términos de la API pública restante. Por supuesto, en un mundo ideal, las pruebas existentes se ocupan de todos los caminos restantes, pero la realidad siempre es un compromiso;)

Warbo
fuente
1
Si bien las otras respuestas son correctas porque el método privado no debería haberse escrito en el ciclo rojo, los humanos cometen errores. Y cuando haya recorrido el camino del error lo suficiente, esta es la solución adecuada.
slebetman
2

Hay momentos en que un método privado podría convertirse en un método público de otra clase.

Por ejemplo, puede tener métodos privados que no sean seguros para subprocesos y dejar la clase en un estado temporal. Estos métodos se pueden mover a una clase separada que se mantiene en privado por su primera clase. Entonces, si su clase es una Cola, podría tener una clase InternalQueue que tenga métodos públicos, y la clase Queue mantenga la instancia InternalQueue de forma privada. Esto le permite probar la cola interna y también deja en claro cuáles son las operaciones individuales en InternalQueue.

(Esto es más obvio cuando imagina que no había una clase de Lista, y si intenta implementar las funciones de Lista como métodos privados en la clase que las usa).

Thomas Andrews
fuente
2
"Hay momentos en que un método privado podría convertirse en un método público de otra clase". No puedo enfatizar eso lo suficiente. A veces, un método privado es simplemente otra clase que grita por su propia identidad.
0

Me pregunto por qué su idioma solo tiene dos niveles de privacidad, totalmente público y completamente privado.

¿Puede organizar que sus métodos no públicos sean accesibles para paquetes o algo así? Luego, coloque sus pruebas en el mismo paquete y disfrute probando el funcionamiento interno que no forma parte de la interfaz pública. Su sistema de compilación excluirá las pruebas al compilar una versión binaria.

Por supuesto, a veces necesita tener métodos verdaderamente privados, no accesibles para nada más que la clase definitoria. Espero que todos estos métodos sean muy pequeños. En general, mantener los métodos pequeños (por ejemplo, menos de 20 líneas) ayuda mucho: las pruebas, el mantenimiento y la simple comprensión del código se vuelven más fáciles.

9000
fuente
3
Cambiar un modificador de acceso al método solo para ejecutar pruebas es una situación en la que una cola mueve a un perro. Creo que probar las partes internas de una unidad solo hace que sea más difícil refactorizar más tarde. Probar la interfaz pública, por el contrario, es excelente porque funciona como un "contrato" para una unidad.
scriptin
No necesita cambiar el nivel de acceso de un método. Lo que estoy tratando de decir es que tiene un nivel de acceso intermedio que permite escribir cierto código, incluidas las pruebas, más fácil, sin hacer un contrato público. Por supuesto , debe probar la interfaz pública, pero adicionalmente a veces es beneficioso probar algunas de las funciones internas de forma aislada.
9000
0

De vez en cuando he topado con métodos privados para protegerlos para permitir pruebas más finas (más estrictas que la API pública expuesta). Esta debería ser la excepción (con suerte muy rara) en lugar de la regla, pero puede ser útil en ciertos casos específicos que puede encontrar. Además, eso es algo que no querría considerar en absoluto al construir una API pública, más de "trampa" que uno puede usar en el software de uso interno en esas situaciones raras.

Brian Knoblauch
fuente
0

Experimenté esto y sentí tu dolor.

Mi solución fue:

deja de tratar las pruebas como construir un monolito.

Recuerde que cuando ha escrito un conjunto de pruebas, digamos 5, para eliminar algunas funcionalidades, no tiene que mantener todas estas pruebas , especialmente cuando esto se convierte en parte de otra cosa.

Por ejemplo, a menudo tengo:

  • prueba de bajo nivel 1
  • código para cumplirlo
  • prueba de bajo nivel 2
  • código para cumplirlo
  • prueba de bajo nivel 3
  • código para cumplirlo
  • prueba de bajo nivel 4
  • código para cumplirlo
  • prueba de bajo nivel 5
  • código para cumplirlo

entonces tengo

  • prueba de bajo nivel 1
  • prueba de bajo nivel 2
  • prueba de bajo nivel 3
  • prueba de bajo nivel 4
  • prueba de bajo nivel 5

Sin embargo, si ahora agrego funciones de nivel superior que lo llaman, que tienen muchas pruebas, ahora podría reducir esas pruebas de bajo nivel para que sean:

  • prueba de bajo nivel 1
  • prueba de bajo nivel 5

El diablo está en los detalles y la capacidad de hacerlo dependerá de las circunstancias.

Michael Durrant
fuente
-2

¿El sol gira alrededor de la tierra o la tierra alrededor del sol? Según Einstein, la respuesta es sí, o ambos, ya que ambos modelos difieren solo por el punto de vista, del mismo modo, la encapsulación y el desarrollo impulsado por pruebas solo están en conflicto porque creemos que lo están. Nos sentamos aquí como Galileo y el papa, lanzándonos insultos el uno al otro: tonto, ¿no ves que los métodos privados también necesitan pruebas; hereje, no rompa la encapsulación! Del mismo modo, cuando reconocemos que la verdad es más grandiosa de lo que pensamos, ¿podemos intentar algo como encapsular las pruebas de las interfaces privadas para que las pruebas de las interfaces públicas no rompan la encapsulación?

Intente esto: agregue dos métodos, uno que no tiene entrada pero que justs devuelve el número de pruebas privadas y otro que toma un número de prueba como parámetro y devuelve pasar / fallar.

hildred
fuente
1
Los insultos elegidos fueron utilizados por Galileo y El Papa, no por ninguna respuesta a esta pregunta.
hildred