En el mundo real, es perfectamente normal escribir pruebas unitarias para el código de otra persona. Claro, el desarrollador original ya debería haber hecho esto, pero a menudo recibe código heredado donde esto simplemente no se hizo. Por cierto, no importa si ese código heredado vino hace décadas de una galaxia muy, muy lejana, o si uno de tus compañeros de trabajo lo revisó la semana pasada, o si lo escribiste hoy, el código heredado es código sin pruebas
Pregúntese: ¿por qué escribimos pruebas unitarias? Going Green obviamente es solo un medio para un fin, el objetivo final es demostrar o refutar las afirmaciones sobre el código que se está probando.
Digamos que tiene un método que calcula la raíz cuadrada de un número de coma flotante. En Java, la interfaz lo definiría como:
public double squareRoot(double number);
No importa si escribió la implementación o si alguien más lo hizo, desea afirmar algunas propiedades de squareRoot:
- que puede devolver raíces simples como sqrt (4.0)
- que puede encontrar una raíz real como sqrt (2.0) con una precisión razonable
- que encuentra que sqrt (0.0) es 0.0
- que arroja una IllegalArgumentException cuando se alimenta un número negativo, es decir, en sqrt (-1.0)
Entonces comienzas a escribir esto como pruebas individuales:
@Test
public void canFindSimpleRoot() {
assertEquals(2, squareRoot(4), epsilon);
}
Vaya, esta prueba ya falla:
java.lang.AssertionError: Use assertEquals(expected, actual, delta) to compare floating-point numbers
Te olvidaste de la aritmética de coma flotante. OK, presentas double epsilon=0.01y listo:
@Test
public void canFindSimpleRootToEpsilonPrecision() {
assertEquals(2, squareRoot(4), epsilon);
}
y agregue las otras pruebas: finalmente
@Test
@ExpectedException(IllegalArgumentException.class)
public void throwsExceptionOnNegativeInput() {
assertEquals(-1, squareRoot(-1), epsilon);
}
y vaya, otra vez:
java.lang.AssertionError: expected:<-1.0> but was:<NaN>
Deberías haber probado:
@Test
public void returnsNaNOnNegativeInput() {
assertEquals(Double.NaN, squareRoot(-1), epsilon);
}
Que hemos hecho aqui Comenzamos con algunas suposiciones sobre cómo debería comportarse el método, y descubrimos que no todas eran ciertas. Luego hicimos el conjunto de pruebas Verde, para anotar la prueba de que el método se comporta de acuerdo con nuestros supuestos corregidos. Ahora los clientes de este código pueden confiar en este comportamiento. Si alguien cambiara la implementación real de squareRoot con algo más, algo que, por ejemplo, realmente arrojó una excepción en lugar de devolver NaN, nuestras pruebas detectarían esto de inmediato.
Este ejemplo es trivial, pero a menudo hereda grandes fragmentos de código donde no está claro lo que realmente hace. En ese caso, es normal colocar un arnés de prueba alrededor del código. Comience con algunos supuestos básicos sobre cómo debe comportarse el código, escriba pruebas unitarias para ellos, pruebe. Si es verde, bien, escribe más pruebas. Si es rojo, bueno, ahora tiene una afirmación fallida de que puede mantener una especificación. Tal vez hay un error en el código heredado. Tal vez la especificación no está clara sobre esta entrada en particular. Tal vez no tienes una especificación. En ese caso, vuelva a escribir la prueba de modo que documente el comportamiento inesperado:
@Test
public void throwsNoExceptionOnNegativeInput() {
assertNotNull(squareRoot(-1)); // Shouldn't this fail?
}
Con el tiempo, terminas con un arnés de prueba que documenta cómo se comporta realmente el código y se convierte en una especie de especificación codificada. Si alguna vez desea cambiar el código heredado, o reemplazarlo con otra cosa, tiene el arnés de prueba para verificar que el nuevo código se comporta de la misma manera, o que el nuevo código se comporta de manera diferente en las formas esperadas y controladas (por ejemplo, que realmente corrige el error que esperas que solucione). Este arnés no tiene que estar completo el primer día, de hecho, tener un arnés incompleto es casi siempre mejor que no tener ningún arnés. Tener un arnés significa que puede escribir su código de cliente con más facilidad, sabe dónde esperar que las cosas se rompan cuando cambia algo, y dónde se rompieron cuando finalmente lo hicieron.
Debe tratar de salir de la mentalidad de que tiene que escribir pruebas unitarias solo porque tiene que hacerlo, como si completara los campos obligatorios en un formulario. Y no debe escribir pruebas unitarias solo para que la línea roja sea verde. Las pruebas unitarias no son tus enemigos, las pruebas unitarias son tus amigos.