¿Escribe pruebas unitarias todo el tiempo en TDD?

8

He estado diseñando y desarrollando código con estilo TDD durante mucho tiempo. Lo que me molesta acerca de TDD es escribir pruebas para el código que no contiene ninguna lógica comercial o comportamiento interesante. Sé que TDD es una actividad de diseño más que pruebas, pero a veces siento que es inútil escribir pruebas en estos escenarios.

Por ejemplo, tengo un escenario simple como "Cuando el usuario hace clic en el botón de verificación, debería verificar la validez del archivo" . Para este escenario, generalmente comienzo a escribir pruebas para la clase de presentador / controlador como la siguiente.

@Test
public void when_user_clicks_check_it_should_check_selected_file_validity(){
    MediaService service =mock(MediaService);
    View view =mock(View);

    when(view.getSelectedFile).thenReturns("c:\\Dir\\file.avi");

    MediaController controller =new MediaController(service,view);
    controller.check();

    verify(service).check("c:\\Dir\\file.avi");
}

Como puede ver, no hay una decisión de diseño o un código interesante para verificar el comportamiento. Estoy probando valores desde la vista pasada a MediaService. Normalmente escribo pero no me gustan este tipo de pruebas. ¿Qué haces con estas situaciones? ¿Escribes exámenes todo el tiempo?

ACTUALIZACIÓN

He cambiado el nombre y el código de la prueba después de las quejas. Algunos usuarios dijeron que debería escribir pruebas para los casos triviales como este para que en el futuro alguien pueda agregar un comportamiento interesante. Pero, ¿qué pasa con "Código para hoy, diseño para mañana". ? Si alguien, incluido yo mismo, agrega un código más interesante en el futuro, entonces se puede crear la prueba para ello. ¿Por qué debería hacerlo ahora para los casos triviales?

caltuntas
fuente
1
"Algunos usuarios dijeron que debería escribir pruebas para los casos triviales como este para que en el futuro alguien pueda agregar un comportamiento interesante. Pero qué pasa con" Código para hoy, diseño para mañana ". "¡Refutación brillante! +1
maple_shaft

Respuestas:

5

No busco el 100% de la cobertura del código. Y generalmente no escribo pruebas de métodos que obviamente no contendrán ninguna lógica comercial y / o más que unas pocas líneas de código. Pero todavía escribo pruebas unitarias (usando TDD) de métodos que no parecen tan complejos. Esto se debe principalmente a que me gusta tener la prueba de la unidad ya, cuando vuelvo a ese código meses o incluso años después, y quiero hacerlo más complejo. Siempre es más fácil extender las pruebas existentes, que tener que construirlo todo desde cero. Como dijo Noufal, es subjetivo. Mi consejo es escribir las pruebas, si cree que el método es un poco complejo o tiene el potencial de volverse más complejo.


fuente
2

Esta es la segunda pregunta de TDD hoy que transmite molestia por la cantidad de pruebas que se escribirán.

"Pruébelo solo si desea que funcione".

No estoy realmente seguro de entender la prueba en la pregunta.

¿Está comprobando que Controller.Check () delega al servicio (dependencia) con el argumento como el valor de archivo seleccionado de la vista? Si es así, esta es una buena prueba. Le permite probar el controlador sin la implementación real del servicio. (micro-pruebas basadas en la interacción).

Actualización: ahora que tengo claro lo que está intentando probar, probablemente movería algo de código y cambiaría el nombre de algunas cosas para que diga "Test Media Controller delega la verificación de archivos seleccionados al servicio de medios". - que es una especificación válida para el controlador.

public class TestMediaController

@Test
public void DelegatesSelectedFileCheckToMediaService(){
    string selectedMediaFileInView = "c:\\Dir\\file.avi";

    when(_view.getSelectedFile).thenReturns(selectedMediaFileInView);

    new MediaController(_service, _view).check();

    verify(_service).check(selectedMediaFileInView);
}
Gishu
fuente
Sí Delega a la clase de servicio.
Pero pregunta: ¿vale la pena escribir esta prueba para verificar solo el parámetro pasado de la vista a la clase de servicio?
@ Sí: prueba que check () está conectado correctamente. Debería haber otra prueba de que Service.Check () funciona una vez que recibe la llamada. Esta prueba parece trivial hoy, pero a medida que pasa el tiempo, alguien podría agregar más código a este método (por ejemplo, una cláusula de protección si alguna condición regresa) y romper el comportamiento existente. Esta prueba protege contra eso.
Gishu
Ok, entendí tu punto, pero ¿qué pasa con "Código para hoy, diseño para mañana"? ? Si alguien o yo agregamos código más interesante en el futuro, entonces puedo crear una prueba para ello. ¿Por qué debería hacerlo ahora para los casos triviales?
1
@mcaaltuntas: el objetivo de TDD es que pruebe la especificación actual, de modo que si un cambio de código futuro ya no satisface la especificación actual, se enterará. No importa cuán fácil cree que es implementar la especificación ahora, alguien podría cambiar el código en el futuro sin prestar atención a este requisito específico, en cuyo caso podrían romperlo y no se darán cuenta de que necesitan agregarle una prueba. . Si no escribe la prueba ahora, entonces no está haciendo TDD. Si no desea probar toda la especificación, es su decisión, pero no está aplicando TDD a esa parte.
Steve Jessop
1

No escribiría una prueba así (o al menos no lo nombraría así). En cambio, escribiría una prueba para la función que requiere esa llamada check(), de modo que si esa verificación o una acción equivalente no se realiza, la función de alto nivel no funcionaría. ¿ Por qué su código necesita llamar al check()método?

En general, trato de mantener las pruebas desacopladas de los detalles de implementación, de modo que al menos el nombre de la prueba solo se refiera a las características externas proporcionadas por el objeto. Los detalles de implementación como objetos y métodos no se mencionan en el nombre de la prueba.

Esto facilita la refactorización (no debería ser necesario cambiar las pruebas cuando cambia la implementación) y también hará que sea más fácil averiguar si una prueba está desactualizada (la característica que especifica ya no es necesaria) . También hará que sea más fácil notar un código innecesario / inactivo, ya que las repeticiones de bajo nivel (como getters y setters) solo se agregarán / conservarán si son requeridas por características de nivel superior.

Esko Luontola
fuente
Ok. Tiene razón acerca de los nombres, puede pensar en su nombre "cuando el usuario hace clic, verifique que debería verificar las propiedades del archivo seleccionado". Pero la pregunta no se trata de nombrar ¿Escribe pruebas para el código "obvio" o no
He cambiado el nombre de la prueba pero la pregunta aún permanece.
¿Qué debe hacer el sistema, si esa verificación falla? ¿Lanzar una excepción o algo más? Probablemente escribiría dos pruebas: qué sucede cuando el archivo es válido y cuándo no es válido. Si hay muchas formas en que un archivo puede ser inválido, escribiría esas pruebas directamente en MediaService.
Esko Luontola
Sí, Probaby también escribiría dos pruebas, pero no contra la clase Controller. Las escribiría contra (como dijiste) la clase MediaService. Entonces, en realidad, la prueba solo verifica que la clase de servicio se haya llamado con parámetros desde la vista. ¿Crees que vale la pena escribir esta prueba para verificar solo los argumentos pasados ​​de la vista a la clase de servicio?
"¿Crees que vale la pena escribir esta prueba para verificar solo los argumentos pasados ​​de la vista a la clase de servicio?" Depende. Si hay pruebas de extremo a extremo que se aseguran de que la verificación funcione (es decir, da un mensaje de error o algo cuando el archivo no es válido), entonces para las pruebas de la unidad del controlador puede ser suficiente simplemente verificar que el método se llama; daría una protección más rápida durante la refactorización que las pruebas de extremo a extremo.
Esko Luontola
0

Esto es subjetivo. No siempre hago TDD, pero cuando lo hago, trato de mantener la cobertura del código como métrica para determinar si mis pruebas son exhaustivas o no. A veces, me vuelvo perezoso y simplemente salteo partes que me parecen "obvias". A veces, violo el ciclo Rojo, Verde, Refactorizador y escribo más código del necesario, pero con el tiempo, he entrado en un ritmo del que me siento cómodo.

Noufal Ibrahim
fuente
0

Interacciones entre clases como la anterior y el original más simple para el que escribiría una prueba. Las interacciones pueden volverse más complejas con el tiempo, por lo que es bueno contar con el trabajo de base.

mlk
fuente
0

Si alguien o yo agregamos código más interesante en el futuro, entonces puedo crear una prueba para ello. ¿Por qué debería hacerlo ahora para los casos triviales?

Asume que en el futuro alguien sabrá que este elemento de la interfaz de usuario existe y lo que llama en el backend.

Mi proyecto actual tiene más de 30 desarrolladores en seis equipos distintos dentro de la misma base de código. Las adiciones triviales de IU desaparecen en la niebla todo el tiempo. Nadie volverá y agregará un caso de prueba para esto más tarde porque nadie recordará que está allí y cuando se rompa la demanda se convierte en "¿Por qué no escribieron una prueba? ¡Hubiera sido tan simple!"

Christopher Bibbs
fuente
0

Como siempre...

Depende

Es difícil ver la utilidad de esta prueba en particular en un contexto TDD porque no conocemos la historia.

Si la historia es Como [usuario de medios] quiero [poder verificar la validez de los medios] Para que [sepa cuándo un archivo no está disponible]

luego el escenario Dado [un botón de verificación de medios] Cuando [el usuario hace clic en el botón] Entonces [se verifica la validez del archivo]

tiene sentido. Es trivial, pero tiene sentido.

Si la historia general es mayor que esta, entonces la definición del escenario puede ser demasiado limitada.

Recuerda:

TDD! = Prueba de unidad

TDD prueba características . Si es una característica, entonces merece una prueba para verificarla.

Steven A. Lowe
fuente