Su pregunta expone una de las partes más difíciles de las pruebas para los desarrolladores que solo se involucran:
"¿Qué demonios pruebo?"
Su ejemplo no es muy interesante porque simplemente pega algunas llamadas de API juntas, por lo que si escribiera una prueba unitaria para ello, terminaría simplemente afirmando que se llamaron a los métodos. Pruebas como esta combinan estrechamente los detalles de su implementación con la prueba. ¡Esto es malo porque ahora tiene que cambiar la prueba cada vez que cambia los detalles de implementación de su método porque cambiar los detalles de implementación interrumpe su (s) prueba (s)!
Tener malas pruebas es en realidad peor que no tener ninguna prueba.
En tu ejemplo:
void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
string path = zipper.Unzip(theZipFile);
IFakeFile file = fileSystem.Open(path);
runner.Run(file);
}
Si bien puede pasar simulacros, no hay lógica en el método para probar. Si intentara una prueba unitaria para esto, podría verse así:
// Assuming that zipper, fileSystem, and runner are mocks
void testDoIt()
{
// mock behavior of the mock objects
when(zipper.Unzip(any(File.class)).thenReturn("some path");
when(fileSystem.Open("some path")).thenReturn(mock(IFakeFile.class));
// run the test
someObject.DoIt(zipper, fileSystem, runner);
// verify things were called
verify(zipper).Unzip(any(File.class));
verify(fileSystem).Open("some path"));
verify(runner).Run(file);
}
Felicitaciones, básicamente copió y pegó los detalles de implementación de su DoIt()
método en una prueba. Feliz mantenimiento
Cuando escribes pruebas quieres probar el QUÉ y no el CÓMO .
Ver Prueba de caja negra para más.
El WHAT es el nombre de su método (o al menos debería serlo). El CÓMO son todos los pequeños detalles de implementación que viven dentro de su método. Las buenas pruebas le permiten cambiar el CÓMO sin romper el QUÉ .
Piénselo de esta manera, pregúntese:
"Si cambio los detalles de implementación de este método (sin alterar el contrato público) ¿se romperán mis pruebas?"
Si la respuesta es sí, está probando el CÓMO y no el QUÉ .
Para responder a su pregunta específica sobre la prueba de código con dependencias del sistema de archivos, supongamos que tiene algo más interesante que hacer con un archivo y desea guardar el contenido codificado de Base64 de byte[]
a en un archivo. Puede usar transmisiones para esto para probar que su código hace lo correcto sin tener que verificar cómo lo hace. Un ejemplo podría ser algo como esto (en Java):
interface StreamFactory {
OutputStream outStream();
InputStream inStream();
}
class Base64FileWriter {
public void write(byte[] contents, StreamFactory streamFactory) {
OutputStream outputStream = streamFactory.outStream();
outputStream.write(Base64.encodeBase64(contents));
}
}
@Test
public void save_shouldBase64EncodeContents() {
OutputStream outputStream = new ByteArrayOutputStream();
StreamFactory streamFactory = mock(StreamFactory.class);
when(streamFactory.outStream()).thenReturn(outputStream);
// Run the method under test
Base64FileWriter fileWriter = new Base64FileWriter();
fileWriter.write("Man".getBytes(), streamFactory);
// Assert we saved the base64 encoded contents
assertThat(outputStream.toString()).isEqualTo("TWFu");
}
La prueba utiliza un ByteArrayOutputStream
sino en la aplicación (mediante la inyección de dependencias) del StreamFactory real (tal vez llamado FileStreamFactory) devolvería FileOutputStream
desde outputStream()
y escribiría a una File
.
Lo interesante del write
método aquí es que estaba escribiendo los contenidos codificados en Base64, así que eso es lo que probamos. Para su DoIt()
método, esto se probaría más adecuadamente con una prueba de integración .
myDll.InvokeSomeSpecialMethod();
donde verificaría que funciona correctamente tanto en situaciones de éxito como de fracaso, por lo que no haría una prueba unitaria,DoIt
peroDllRunner.Run
dicho mal uso de una prueba UNIT para verificar que todo el proceso funcione un mal uso aceptable y, como sería una prueba de integración enmascarando una prueba unitaria, las reglas normales de la prueba unitaria no necesitan aplicarse estrictamente