Modificaciones de Archivos de Pruebas Unitarias


Una tarea común en los programas en los que he estado trabajando últimamente es modificar un archivo de texto de alguna manera. (Hey, estoy en Linux. Todo es un archivo. Y hago administración de sistemas a gran escala.)

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

He leído acerca de las pruebas unitarias en Dive Into Python, y está bastante claro lo que quiero hacer al probar una aplicación que convierte números decimales a romanos (el ejemplo en DintoP). La prueba es muy autónoma. No necesita verificar que el programa IMPRIME lo correcto, solo necesita verificar que las funciones devuelven la salida correcta a una entrada dada.

En mi caso, sin embargo, necesitamos probar que el programa está modificando su entorno correctamente. Esto es lo que se me ha ocurrido:

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

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

3) Verifique que el archivo en /tmp se haya cambiado correctamente; pase/suspenda la prueba unitaria en consecuencia.

Esto me parece complicado. (Obtiene incluso kludgier si desea verificar que las copias de seguridad del archivo se crean correctamente, etc.) ¿Se le ha ocurrido a alguien una manera mejor?

Author: Schof, 2008-09-20

6 answers

Estás hablando de probar demasiado a la vez. Si comienzas a intentar atacar un problema de prueba diciendo "Vamos a verificar que modifica su entorno correctamente", estás 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, ¿vas a tener una función que determine dónde están los archivos que tienen que ser escritos? ¿Cuáles son las entradas a esa función? Tal vez un variable de entorno, tal vez algunos valores leídos desde un archivo de configuración? Pruebe esa función, y no haga realmente nada que modifique el sistema de archivos. No le pases valores "realistas", sino valores que sean fáciles de verificar. Cree un directorio temporal, rellénelo con archivos en el método setUp de su prueba.

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

En última instancia, tendrá que probar la función final de nivel superior everything-is-actually-hooked-up-for-real que pasa la variable de entorno real y el archivo de configuración real y pone todo junto. Pero no te preocupes por eso para empezar. Por un lado, usted comenzará recoger trucos a medida que escribes pruebas individuales para funciones más pequeñas y crear simulaciones de prueba, falsificaciones y stubs se convertirá en una segunda naturaleza para ti. Para otro: incluso si no puede averiguar cómo probar esa llamada a una función, tendrá un nivel muy alto de confianza de que todo lo que está llamando funciona perfectamente. Además, notará que el desarrollo basado en pruebas lo obliga a hacer que sus API sean más claras y flexibles. Por ejemplo: es mucho más fácil probar algo que llama a un open() método en un objeto que vino de algún lugar abstracto, que para probar algo que llama os.open en una cadena que se 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 captar qué métodos se llaman en ella.

También puede crear herramientas de prueba para facilitar las tareas repetitivas. Por ejemplo, twisted proporciona facilidades para crear archivos temporales para probar integrados directamente en su herramienta de prueba . No es raro que las herramientas de prueba o proyectos más grandes con sus propias bibliotecas de prueba tengan una funcionalidad como esta.

 14
Author: Glyph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-21 15:16:34

Tienes dos niveles de prueba.

  1. Filtrar y Modificar 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, copia de seguridad. Lo siento, pero esas son las operaciones del sistema de archivos que require bueno require requieren un sistema de archivos adecuado para las pruebas.

Para este tipo de probando, a menudo usamos un objeto "Simulado". Puede diseñar una clase "FileSystemOperations" que encarne las diversas operaciones del sistema de archivos. Usted prueba esto para asegurarse de que lo hace básico leer, escribir, copiar, cambiar el nombre, etc. No hay lógica real en esto. Solo métodos que invocan operaciones del sistema de archivos.

A continuación, puede crear un MockFileSystem que dummies a cabo las diversas operaciones. Puedes usar este objeto Simulado para probar tus otras clases.

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

Ponga su módulo MockOS en el PYTHONPATH y puede ocultar el módulo del sistema operativo real.

Para las operaciones de producción utiliza sus clases "Logic" bien probadas más su clase FileSystemOperations (o el módulo real OS).)

 7
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 02:05:23

Para lectores posteriores que solo quieren una forma de probar que la escritura de código en archivos funciona correctamente, aquí hay un "fake_open" que parchea el builtin abierto de un módulo para usar StringIO. fake_open devuelve un dict de archivos abiertos que pueden ser examinados en una prueba unitaria o doctest, todo 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
 3
Author: Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-08 10:45:40

Cuando toco archivos en mi código, tiendo a preferir burlarme de la lectura y escritura real del archivo... entonces puedo dar a mis clases el contenido exacto que quiero en la prueba, y luego afirmar que la prueba está escribiendo de nuevo el contenido 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 burlarse del uso de un archivo real.

Para esto, puede intentar pasar secuencias y a continuación, simplemente pase una secuencia de entrada/salida de cadena simple que no escriba en un archivo, o tenga una función que haga el real "escribir esta cadena en un archivo" o "leer esta cadena de un archivo", y luego reemplace esa función en sus pruebas.

 2
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 02:01:20

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 'se ve' 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 raíz es simplemente /. Para probar, cree un entorno de sombra bajo /tmp / test y luego ejecute sus scripts con una ruta raíz de / tmp / test.

 1
Author: Rob Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 02:04:47

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

 1
Author: njsf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 02:06:19