Desarrollo guiado por pruebas: ¿Una forma buena / aceptada de probar las operaciones del sistema de archivos?

14

En este momento estoy trabajando en un proyecto que genera una tabla (entre otras cosas) basada en el contenido de un sistema de archivos y, a su vez, realiza algunas modificaciones de metadatos en las cosas que encuentra. La pregunta es: ¿cómo deben escribirse las pruebas en torno a esto, o configurarse? ¿Hay una manera fácil de burlarse de esto? ¿O debería configurar un "sandbox"?

Kirbinator
fuente

Respuestas:

13

Como siempre lo hace en TDD con recursos externos: crea una o más interfaces para las operaciones de su sistema de archivos y las "simula". Desea probar su "generador de tablas" y su código de modificación de metadatos, no las operaciones del sistema de archivos en sí (lo más probable es que esté utilizando implementaciones de biblioteca listas para acceder al sistema de archivos).

Doc Brown
fuente
TDD no recomienda burlarse de la implementación de la unidad bajo prueba. Ver (e, g) solnic.eu/2014/05/22/mocking-and-ruby.html
soru
1
@soru: Eso no es lo que recomienda esta respuesta. Recomienda primero crear interfaces, luego burlarse de la interfaz . Entonces sí prueba la lógica de negocios, pero no la interfaz del sistema de archivos.
sleske
55
Pero parece que la lógica de negocios se define en términos de archivos y directorios. Entonces, lo que llama a la API del sistema de archivos es lo que necesita prueba. Probar cualquier lógica empresarial relacionada sin archivos no necesita burlarse; solo pruébalo.
soru
@soru a la derecha, por lo que crea una capa delgada alrededor de archivos y carpetas con una interfaz definida de modo que todas las operaciones específicas del dominio estén en el lado del cliente, y el lado de la implementación es lo suficientemente trivial como para estar seguro de que funciona sin pruebas unitarias (las pruebas de integración lo harán aún se requiere). Similar a la idea de un diálogo humilde en código ui, o usando un repositorio simulado en código que opera en objetos persistentes.
Jules
2
Así que efectivamente renunciamos a probar la clase real que interactúa con el sistema de archivos, la base de datos, etc. En su lugar, creamos una implementación diferente con la misma interfaz que un simulacro / código auxiliar, pero la clase real la dejamos sin ningún tipo de prueba unitaria, porque creemos que no podemos probarlo unitariamente y, en su lugar, deberíamos hacer pruebas de integración para probarlo. ¿Es esto correcto?
Andrew Savinykh
11

¿Qué tiene de malo tener un sistema de archivos de "prueba"?

Cree una estructura de carpeta / directorio de plantilla que tenga suficiente contenido para probar sus operaciones.

Durante la configuración de su unidad de prueba, copie esta estructura inicial (le recomendaría que comprima la plantilla y la descomprima en su área de prueba). Ejecute sus pruebas. Eliminar todo durante el desmontaje.

El problema con la burla es, en primer lugar, que los sistemas de archivos, los sistemas operativos y las bases de datos que pertenecen a su proyecto realmente no califican como recursos externos y, en segundo lugar, burlarse de las llamadas a sistemas de bajo nivel es lento y propenso a errores.

James Anderson
fuente
55
La burla de las operaciones del sistema de archivos creará pruebas de ejecución mucho (!) Más rápidas que el uso de un sistema de archivos real, y si esto es más "propenso a errores" es discutible, diría que depende de la implementación. Sin embargo, creo que su sugerencia está bien para crear pruebas de integración automatizadas (que normalmente haría primero cuando no estoy haciendo TDD). Pero el OP solicitó específicamente TDD, y las pruebas unitarias TDD tienen que ser rápidas.
Doc Brown
1
Creo que los sistemas de archivos, si se burlan, idealmente deberían tener una API completa escrita y mantenida por algún grupo, porque estás reinventando la rueda, si haces algo significativo con el sistema de archivos.
Frank Hileman
2
@Doc Brown: estoy asumiendo que quiere hacer operaciones de tipo dir, eliminar y cambiar el nombre, todas las cuales tienen casos extremos que serían difíciles de burlar. También en el hardware moderno, descomprimir algunos archivos pequeños en un directorio es solo un poco más lento que cargar una clase Java, después de todo, todo es E / S.
James Anderson el
Cuanto más pienso en el sistema de archivos de prueba, más me gusta.
Frank Hileman
3

Este es el tipo de cosas que definitivamente necesita para la prueba de integración, ya que los sistemas de archivos del mundo real tienen todo tipo de comportamiento extraño (como la forma en que Windows no permitirá eliminar un archivo si algún proceso, incluido el eliminador, lo tiene abierto).

Entonces, el enfoque de TDD es escribir primero la prueba de integración (TDD, estrictamente hablando, no tiene conceptos distintos de 'prueba unitaria' y 'prueba de integración'; son solo pruebas). Muy probablemente eso sea suficiente; así que trabajo hecho, detente, vete a casa .

Si no, habrá cierta complejidad interna que no es fácil de probar adecuadamente organizando los archivos. En cuyo caso, simplemente elimina esa complejidad, la coloca en una clase y escribe pruebas unitarias para esa clase . Es muy probable que encuentre que esa clase común también se puede usar en la base de datos, archivos xml, etc.

En ningún caso tomaría el núcleo fundamental del código que está escribiendo y lo 'burlará' para escribir pruebas que pasarán si la unidad bajo prueba está o no equivocada.

soru
fuente
Esta respuesta realmente lo puso en perspectiva para mí: 'unit test' and 'integration test'; they are just tests.creo que de manera realista, esta será la mejor solución para mi caso, realmente necesito probar las bibliotecas del sistema de archivos que estoy usando para casos extremos y cómo debería responder la aplicación aquellos. Si cambio a una biblioteca de sistema de archivos diferente, no quiero tener que volver a escribir un montón de simulacros / código de prueba para trabajar con la nueva biblioteca, pero tener una estructura de carpetas de prueba y pruebas de integración lo haría mucho más simple.
tehDorf
2

Entiendo su pregunta como "Una forma buena / aceptada de probar una clase que depende de las operaciones del sistema de archivos". No asumo que quieres probar el sistema de archivos de tu sistema operativo.

Para mantener el esfuerzo de 'interactuar con las operaciones de su sistema de archivos y "burlarse de ellos", como sugirió la respuesta de @Doc Brown lo más pequeño posible, es una buena idea usar flujos binarios de Java o un lector de texto (o su equivalente en c # o el lenguaje de programación que está usando) en lugar de usar Archivos con nombres de archivo directamente en su clase desarrollada por tdd.

Ejemplo:

Usando Java he implementado una clase CsvReader

public class CsvReader {
    private Reader reader;

    public CsvReader(Reader reader) {
        this.reader = reader;
    }
}

Para probar, utilicé datos de memoria como este

String contentOfCsv = "TestColumn1;TestColumn2\n"+
    "value1;value2\n";

CsvReader sut = new CsvReader(java.io.StringReader(contentOfCsv));

o incluir datos de prueba en los recursos

CsvReader sut = new CsvReader(getClass().getResourceAsStream("/data.csv"));

En producción uso el sistema de archivos

CsvReader sut = new CsvReader(new BufferedReader( new FileReader( "/import/Prices.csv" ) ));

De esta forma, mi CsvReader no depende del sistema de archivos sino de un "Lector" de abstracción donde hay una implementación para el sistema de archivos.

k3b
fuente
2
El único problema aquí es que el PO no estaba hablando de las operaciones de archivo, pero el sistema de archivos operaciones y operaciones de metadatos - supongo que quería decir algo así como una lista de todos los archivos en un directorio, actualizar alguna información EXIF en todos los archivos de imagen, etc.
Doc Brown
Esto es correcto.
Kirbinator
1
Puede crear IDirectoryLister que tiene un método String [] List (directorio de cadenas); entonces FakeDirectoryLister puede implementar ese método simplemente devolviendo String [] {".", "..", "foo.bat", "bar.exe"};
Anders Lindén
0

Cree un contenedor para las operaciones del sistema de archivos. En las pruebas, pase un simulacro que implemente la misma interfaz que el contenedor. En producción, pase la envoltura.

jhewlett
fuente