¿Es útil para los métodos de prueba unitaria donde la única lógica son los guardias?

12

Digamos que tengo un método como este:

public void OrderNewWidget(Widget widget)
{
   if ((widget.PartNumber > 0) && (widget.PartAvailable))
   {
        WigdetOrderingService.OrderNewWidgetAsync(widget.PartNumber);
   }
}

Tengo varios de estos métodos en mi código (la mitad frontal de una llamada asíncrona al servicio web).

Estoy debatiendo si es útil cubrirlos con pruebas unitarias. Sí, aquí hay lógica, pero solo es lógica de guardia. (Lo que significa que me aseguro de tener las cosas que necesito antes de permitir que se realice la llamada al servicio web).

Una parte de mí dice "claro, puedes probarlos por unidad, pero no vale la pena" (estoy en un proyecto que ya está retrasado).

Pero el otro lado de mí dice que si no los prueba unitariamente y alguien cambia a los Guardias, entonces podría haber problemas.

Pero la primera parte de mí dice que si alguien cambia a los guardias, entonces solo estás haciendo más trabajo para ellos (porque ahora tienen que cambiar los guardias y las pruebas unitarias para los guardias).

Por ejemplo, si mi servicio asume la responsabilidad de verificar la disponibilidad del Widget, es posible que ya no quiera ese guardia. Si está bajo prueba unitaria, tengo que cambiar dos lugares ahora.

Veo pros y contras en ambos sentidos. Así que pensé en preguntar qué habían hecho otros.

Vaccano
fuente
17
No estás haciendo "más trabajo" para los mantenedores. Si cambian la lógica, deben cambiar las pruebas unitarias correspondientes. Asi es como funciona. No veo sus contras: si su prueba unitaria no requiriera cambio, entonces no probaría nada, ¿verdad? También podría preguntarse si las pruebas unitarias son útiles en absoluto.
Andres F.
99
Esto está fuera de tema, pero cambiaría la lógica para lanzar una excepción si el número de parte es 0 o menos, o si la parte no está disponible, ya que en mi opinión sería un error permitir que alguien llame a ese método con un widget falso, enmascarando en silencio otro problema.
Mateo
2
@Matthew muy buen punto. Esta función miente. El nombre te dice que va a pedir algo. Y luego no lo hace, pero nunca lo sabrás, a menos que apliques la misma lógica que está dentro, lo que infringe DRY. En otras palabras: si el diseño se cambia para que sea más correcto, esta pregunta tal vez no se hubiera hecho en primer lugar.
stijn
2
but it is not worth the time" (I am on a project that is already behind schedule).Somos desarrolladores de software. El único momento en que estamos programados es cuando estamos muertos :)
maple_shaft

Respuestas:

26

Una parte de mí dice "claro, puedes probarlos por unidad, pero no vale la pena" (estoy en un proyecto que ya está retrasado).

Son tres pruebas muy cortas. Pasaste tanto tiempo haciéndote la pregunta.

Pero el otro lado de mí dice que si no los prueba unitariamente y alguien cambia a los Guardias, entonces podría haber problemas.

Escucha a este lado.

Pero la primera parte de mí dice que si alguien cambia a los guardias, entonces solo estás haciendo más trabajo para ellos (porque ahora tienen que cambiar los guardias y las pruebas unitarias para los guardias).

Si su mantenedor es un loco de TDD, lo está haciendo más difícil para ellos. Cualquier cambio que realice sin que haya un cambio relacionado o la adición de pruebas me lleva a pensar mucho. De hecho, probablemente agregaría las pruebas antes de seguir adelante y hacer el cambio.

La primera parte de ti está simplemente equivocada. Dale una palmadita en la espalda a la segunda parte y deja de pensar en ello.

pdr
fuente
+1 por concisión aunque escribí una respuesta también je
Jimmy Hoffa
3
+1. Sí, si los requisitos cambian, algunas pruebas tendrán que adaptarse.
Olivier Jacot-Descombes
Si bien me gusta lo que dices, tengo muchas instancias de este tipo de llamadas en mi código (la mitad frontal de una llamada asíncrona). Por lo tanto, no se trata solo de 3 pruebas unitarias. Aún así, si es la forma "correcta", entonces quiero que se hagan.
Vaccano
@ Vaccano: Cuantos más tenga que escribir, más rutas lógicas no tendrá pruebas y más necesario será escribirlas.
pdr
9

Simplificaría las pruebas unitarias si la lógica de protección y el pedido real fueran métodos separados.

En la Widgetclase

public bool IsReadyForOrdering { get { return PartNumber > 0 && PartAvailable; } }

o un método equivalente en otro lugar

public bool IsWidgetReadyForOrdering(Widget widget)
{
    return widget.PartNumber > 0 && widget.PartAvailable;
}

El método de pedido

public void OrderNewWidget(Widget widget)
{
   if (IsWidgetReadyForOrdering(widget)) {
        WigdetOrderingService.OrderNewWidgetAsync(widget.PartNumber);
   }
}

Ahora las pruebas se IsWidgetReadyForOrderingvolvieron fáciles. No lo pienses mucho más. ¡Pruébalo!

Olivier Jacot-Descombes
fuente
1
Oof, lógica con estado en una propiedad ... -1 que debería ser un método y debería tomar PartNumber, PartAvailable debido a su simplicidad. Haga que esté protegido si no necesita ser parte de la API pública pero no una propiedad. Además, generalmente no estoy de acuerdo. La lógica de protección dentro del método está totalmente bien, y en mi opinión es mejor porque es un método tan pequeño que solo estás contaminando la clase para hacer que un método ya pequeño sea más pequeño. Agréguelo a la clase en este caso solo si es lógica repetitiva.
Jimmy Hoffa
1
Primero, esto no responde la pregunta. En segundo lugar, es falso. Necesitas escribir tres pruebas de cualquier manera. Tercero, expone los detalles de implementación al código de llamada, innecesariamente.
pdr
8
@JimmyHoffa: ¿dónde ves alguna lógica con estado en esa propiedad? De hecho, el ejemplo muestra cómo separar la lógica de cambio de estado (OrderNewWidget) de la lógica que no cambia de estado (ReadyForOrdering). Y creo firmemente que extraer incluso esas funciones pequeñas mejorará el código y no lo empeorará, como usted dice. Entonces +1.
Doc Brown
2
El método OrderNewWidgetprobablemente esté en otra clase que Widget, ya que tiene un Widgetargumento. Como el método no tiene valor de retorno, probarlo no es obvio. Tendría que inyectar una WigdetOrderingService-mock que rastrea la OrderNewWidgetAsyncllamada.
Olivier Jacot-Descombes
2
@JimmyHoffa: en realidad, su definición de "sin estado" significa "estática" en C #. Por lo tanto, lo que usted dice es "las propiedades no deberían acceder al estado de un objeto". Pero incluso los captadores tontos acceden a las variables de estado, por lo que eso significaría "escribir solo propiedades estáticas", lo que no parece tener mucho sentido.
Doc Brown
6

Si no tiene tiempo en su cronograma para las pruebas unitarias, pero tiene tiempo reservado para un uso sólido del control de calidad, pregunte si puede robar algo de ese tiempo de control de calidad para escribir pruebas unitarias, o si puede pasar parte del período de control de calidad haciendo pruebas unitarias, o tal vez simplemente lidiando con un código no probado. Desafortunadamente, los horarios que son inamovibles lo obligan a hacer concesiones o trabajar hasta la muerte, generalmente sugiero la primera opción porque la segunda lo hará incapaz de apoyar / manteniendo el sistema correctamente por el término del mismo.

Dicho esto, a su pregunta general de probar las declaraciones de guardia; ¡Si! Absolutamente prueba declaraciones de guardia! Esas son partes importantes del comportamiento de ese método, no querrás descubrir que alguien malinterpretó algo haciendo una corrección de errores y eliminó a tus guardias o cambiaste &&a uno, ||¿verdad? Las pruebas unitarias asegurarán que a) usted realmente tenga la lógica correcta en sus guardias yb) nadie rompa esa lógica más tarde sin recibir una queja cuando ejecutan las pruebas unitarias diciéndoles que debería ser así por alguna razón.

Jimmy Hoffa
fuente
0

Hay algunas respuestas excelentes anteriores, y los puntos que hacen son muy importantes. Pero uno que parece haberse perdido es que tiene un conjunto completo de pruebas unitarias, que se leen como una especificación extremadamente detallada del código. Si omite la validación de prueba solo porque el código es tan fácil que es difícil ver cómo podría salir mal, falta parte de su especificación. Si me uniera a un equipo donde estas pruebas se habían perdido, en realidad asumiría que su servicio no estaba validando sus argumentos.

Jules
fuente
-5

El código es el código. Debe intentar obtener una cobertura del 100% al realizar la prueba. Si no fuera importante, no estaría allí.

Superusuario
fuente