¿Las pruebas unitarias de una sola afirmación no rompen el principio DRY?

12

Cada vez que escribo pruebas unitarias, siempre he intentado tener una sola afirmación por prueba para facilitar la depuración cuando las pruebas fallan. Sin embargo, a medida que sigo esta regla, siento que estoy copiando constantemente el mismo código en cada prueba y al tener más pruebas, es más difícil volver a leer y mantener.

Entonces, ¿las pruebas de afirmación única violan DRY?

¿ Y hay una buena regla a seguir para encontrar un buen equilibrio, como solo tener una prueba por método ? *

* Me doy cuenta de que probablemente no haya una solución única para todo esto, pero ¿hay alguna forma recomendada de abordarlo?

Korey Hinton
fuente
55
Puede extraer el código que copia en los métodos
Ismail Badawi
@IsmailBadawi que suena como una buena idea. Supongo que estos métodos deberían devolver una instancia del objeto de clase que estoy probando
Korey Hinton
1
¿Estás creando algo en un estado dado para ser probado? Eso suena como un accesorio.
Dave Hillier
@DaveHillier sí, hoy aprendí una palabra nueva. Gracias :)
Korey Hinton
depende de cómo interpretes 1 aserción por prueba, si te refieres a una llamada Assert *, sí, si también quieres asegurarte de que las invariantes aún se mantengan (luego extrae eso de nuevo en un método), o si hay múltiples efectos que simplemente puedes ' prueba t en una sola aserción (o si lo hizo, entonces no estaría claro por qué no)
trinquete monstruo

Respuestas:

14

Las pruebas unitarias adecuadas tienen una convención de nomenclatura que lo ayuda a identificar de inmediato lo que ha fallado:

public void AddNewCustomer_CustomerExists_ThrowsException()

Es por eso que tiene una afirmación por prueba, de modo que cada método (y su nombre) corresponde a la condición que está afirmando.

Como ha señalado correctamente, cada nueva prueba tendrá un código de configuración similar. Al igual que con cualquier código, puede refactorizar el código común en su propio método para reducir o eliminar la duplicación y hacer que su código sea más SECO. Algunos marcos de prueba están diseñados específicamente para permitirle colocar ese código de configuración en un solo lugar .

En TDD, ninguna prueba es YAGNI, porque escribe pruebas basadas solo en lo que necesita que haga su código. Si no lo necesita, no escribirá la prueba.

Robert Harvey
fuente
Refactorizar en un solo método suena bien. Si tuviera que una instancia de la clase para estar en un determinado estado, podría crear y devolver una nueva instancia de ese objeto en el método refactorizado o, como sugiere, utilice los métodos proporcionados por el marco de prueba para la configuración inicial de la prueba.
Korey Hinton
en su último punto, estoy seguro de que podría escribir pruebas para la funcionalidad Me gustaría que el código tiene, en lugar de la funcionalidad que necesita
joelb
5

Entonces, ¿las pruebas de afirmación única violan DRY?

No, pero promueve la violación.

Dicho esto, un buen diseño orientado a objetos tiende a salir por la ventana para las pruebas unitarias, principalmente por una buena razón. Es más importante que las pruebas unitarias se aíslen unas de otras para que la prueba se pueda interrogar de forma aislada y, si es necesario, se repare con la confianza de que no se rompen otras pruebas. Básicamente, la exactitud y legibilidad de la prueba es más importante que su tamaño o facilidad de mantenimiento.

Francamente, nunca he sido fanático de la única afirmación por regla de prueba por las razones que usted describe: conduce a una gran cantidad de código repetitivo que es difícil de leer, fácil de errar y difícil de corregir si refactoriza (lo que te lleva a refactorizar menos).

Si se supone que una función devuelve una lista de "foo" y "bar" para una entrada dada, pero en cualquier orden, está perfectamente bien usar dos afirmaciones para verificar que ambos están en el conjunto de resultados. Cuando se mete en problemas es cuando una sola prueba está comprobando dos entradas o dos efectos secundarios y no sabe cuál de los dos causó la falla.

Lo veo como una variación del Principio de responsabilidad única: debe haber una sola cosa que puede hacer que una prueba falle, y en un mundo ideal ese cambio solo debe romper una prueba.

Pero al final es una compensación. ¿Es más probable que pase más tiempo manteniendo todo el código de copia pegado, o pasará más tiempo buscando las causas raíz cuando múltiples fuentes pueden romper las pruebas? Mientras escribas -algunas- pruebas, probablemente no importe demasiado. A pesar de mi desdén por las pruebas de una sola afirmación, tiendo a equivocarme con más pruebas. Su experiencia puede ser diferente.

Telastyn
fuente
1
Para las pruebas, es más importante ser DAMP que DRY (frases descriptivas y significativas).
Jörg W Mittag
2

No. Esto parece ser la forma en que lo estás haciendo. A menos que haya encontrado una referencia notable donde afirman que esta es una buena práctica.

Use un dispositivo de prueba (aunque en la terminología de XUnit el conjunto de pruebas, la configuración y el desmantelamiento es el dispositivo), es decir, una configuración o ejemplos que se aplican a todas sus pruebas.

Use métodos como lo haría normalmente para estructurar su código. Cuando se realicen pruebas de refactorización, el TDD Rojo-Verde-Refactor no se aplica, en su lugar, se aplica "Refactorización en Rojo". Es decir,

  1. rompe deliberadamente tu prueba,
  2. haz tu refactorización
  3. arregla tu prueba

De esta manera, sabrá que las pruebas aún dan resultados positivos y negativos.

Existen varios formatos estándar para las pruebas. Por ejemplo, Arrange, Act, Assert o Given When, Then (BDD) . Considere usar una función separada para cada paso. Debería poder llamar a la función para reducir repetitivo.

Dave Hillier
fuente