2008-09-20 12 views
22

Una tarea común en los programas en los que he estado trabajando últimamente es modificar un archivo de texto de alguna manera. (Hola, estoy en Linux. Todo es un archivo. Y administro sistemas a gran escala)Modificaciones de archivo de prueba de unidad

Pero el archivo que modifica el código puede no existir en mi escritorio. Y probablemente no quiera modificarlo si está en mi escritorio.

He leído sobre las pruebas unitarias en Dive Into Python, y es bastante claro lo que quiero hacer al probar una aplicación que convierta números decimales a números romanos (el ejemplo en DintoP). La prueba es muy autónoma. No necesita verificar que el programa IMPRIMA lo correcto, solo necesita verificar que las funciones devuelven el resultado correcto a una entrada determinada.

En mi caso, sin embargo, tenemos que probar que el programa está modificando su entorno correctamente. Esto es lo que se me ocurrió:

1) Cree el archivo "original" en una ubicación estándar, quizás/tmp.

2) Ejecute la función que modifica el archivo, pasándole la ruta al archivo en/tmp.

3) Verifique que el archivo en/tmp se haya cambiado correctamente; pasar/fallar prueba de la unidad en consecuencia.

Esto me parece kludgy. (Obtiene incluso más kludgier si desea verificar que las copias de seguridad del archivo se crean correctamente, etc.) ¿Alguien ha encontrado una manera mejor?

Respuesta

14

Estás hablando de probar demasiado a la vez. Si comienza a tratar de atacar un problema de prueba diciendo "Verifiquemos que modifique su entorno correctamente", está condenado al fracaso. Los entornos tienen docenas, tal vez incluso millones de variaciones potenciales.

En su lugar, mira las piezas ("unidades") de tu programa. Por ejemplo, ¿va a tener una función que determine dónde se deben escribir los archivos? ¿Cuáles son las entradas a esa función? Tal vez una variable de entorno, tal vez algunos valores leídos desde un archivo de configuración? Pruebe esa función y no haga nada que modifique el sistema de archivos. No le pase valores "realistas", pase valores que sean fáciles de verificar. Cree un directorio temporal, rellene con los archivos en el método setUp de su prueba.

Luego pruebe el código que escribe los archivos. Solo asegúrate de escribir los contenidos correctos del archivo de contenido. ¡Ni siquiera escribas en un sistema de archivos real! No necesita crear objetos de archivo "falsos" para esto, solo use los prácticos módulos de Python StringIO; son implementaciones "reales" de la interfaz de "archivo", simplemente no son las que su programa realmente va a escribir.

En última instancia, tendrá que probar la función final de nivel superior que pasa la variable de entorno real y el archivo de configuración real y reúne todo. Pero no te preocupes por eso para comenzar. Por un lado, empezarás a buscar trucos mientras escribes pruebas individuales para funciones más pequeñas y la creación de simulacros de prueba, falsificaciones y talones se convertirá en algo natural para ti. Por otro: incluso si no puede entender cómo probar esa llamada a una función, tendrá un alto nivel de confianza de que todo lo que está llamando funciona perfectamente. Además, notará que el desarrollo basado en pruebas lo fuerza a hacer sus API más claras y más flexibles. Por ejemplo: es mucho más fácil probar algo que llama a un método open() en un objeto que proviene de algún lugar abstracto, que probar algo que llama al os.open en una cadena que lo pasa. El método open es flexible; se puede falsificar, se puede implementar de manera diferente, pero una cadena es una cadena y os.open no le da ningún margen de maniobra para detectar los métodos que se le solicitan.

También puede crear herramientas de prueba para facilitar las tareas repetitivas. Por ejemplo, twisted proporciona facilidades para crear archivos temporales para probar built right into its testing tool. No es raro que las herramientas de prueba o los proyectos más grandes con sus propias bibliotecas de prueba tengan una funcionalidad como esta.

2

Cuando toco archivos en mi código, tiendo a preferir burlarme de la lectura y escritura reales del archivo ... entonces puedo dar a mis clases los contenidos exactos que deseo en la prueba, y luego afirmar que la prueba está escribiendo los contenidos que espero.

He hecho esto en Java, y me imagino que es bastante simple en Python ... pero puede requerir diseñar sus clases/funciones de tal manera que sea FÁCIL simular el uso de un archivo real.

Para esto, puede intentar pasar en las secuencias y luego simplemente pasar en una secuencia de entrada/salida simple que no escribirá en un archivo, o tener una función que realmente "escriba esta cadena en un archivo" o "lea esta cadena desde un archivo", y luego reemplace esa función en sus pruebas.

1

Creo que estás en el camino correcto. Dependiendo de lo que necesite hacer, chroot puede ayudarlo a configurar un entorno para sus scrpits que parezca real, pero no lo es.

Si eso no funciona, entonces podría escribir sus scripts para tomar una ruta 'raíz' como argumento.

En una ejecución de producción, la ruta de la raíz es solo /. Para las pruebas, crea un entorno oculto en/tmp/test y luego ejecuta los scripts con una ruta raíz de/tmp/test.

7

Tiene dos niveles de prueba.

  1. Filtrado y modificación de contenido. Estas son operaciones de "bajo nivel" que realmente no requieren E/S de archivos físicos. Estas son las pruebas, la toma de decisiones, las alternativas, etc. La "lógica" de la aplicación.

  2. Operaciones del sistema de archivos. Crear, copiar, cambiar el nombre, eliminar, hacer una copia de seguridad. Lo sentimos, pero esas son operaciones adecuadas del sistema de archivos que, bueno, requieren un sistema de archivos adecuado para las pruebas.

Para este tipo de prueba, a menudo utilizamos un objeto "Simulado". Puede diseñar una clase "FileSystemOperations" que incorpore las diversas operaciones del sistema de archivos. Usted prueba esto para asegurarse de que lee, escribe, copia, renombra, etc. básicos. No hay una lógica real en esto. Solo métodos que invocan las operaciones del sistema de archivos.

A continuación, puede crear un MockFileSystem que simula las diversas operaciones. Puedes usar este objeto Mock para probar tus otras clases.

En algunos casos, todas las operaciones de su sistema de archivos están en el módulo os. Si ese es el caso, puede crear un módulo MockOS con la versión simulada de las operaciones que realmente usa.

Coloque su módulo MockOS en el PYTHONPATH y puede ocultar el módulo de SO real.

Para las operaciones de producción que utilice sus clases bien probados "lógica", además de su clase FileSystemOperations (o el módulo de sistema operativo real.)

1

Es posible que desee configurar el script de manera que se ejecuta dentro de una jaula chroot, por lo tiene todo el entorno que necesita la prueba, incluso si las rutas y las ubicaciones de archivos están codificadas en el código [no es una buena práctica, pero a veces uno obtiene las ubicaciones de archivos de otros lugares ...] y luego verifica los resultados a través del código de salida .

3

Para los lectores posteriores que solo quieren una manera de probar que la escritura de código en los archivos está funcionando correctamente, aquí hay una "entrada_fácil" que parchea el builtin abierto de un módulo para usar StringIO. fake_open devuelve un dict de archivos abiertos que pueden examinarse en una unidad de prueba o doctest, todo ello sin necesidad de un sistema de archivos real.

def fake_open(module): 
    """Patch module's `open` builtin so that it returns StringIOs instead of 
    creating real files, which is useful for testing. Returns a dict that maps 
    opened file names to StringIO objects.""" 
    from contextlib import closing 
    from StringIO import StringIO 
    streams = {} 
    def fakeopen(filename,mode): 
     stream = StringIO() 
     stream.close = lambda: None 
     streams[filename] = stream 
     return closing(stream) 
    module.open = fakeopen 
    return streams