2009-07-06 8 views
108

¿Existen bibliotecas o métodos para simular el sistema de archivos en C# para escribir pruebas unitarias? En mi caso actual, tengo métodos que verifican si existe cierto archivo y lee la fecha de creación. Es posible que necesite más que eso en el futuro.¿Cómo se burla del sistema de archivos en C# para las pruebas unitarias?

+1

Esto parece un duplicado de varias otras, incluyendo: http://stackoverflow.com/questions/ 664277/needed-file-system-interfaces-and-implementation-in-net. –

+0

Quizás intente buscar en pex (http://research.microsoft.com/en-us/projects/pex/filesystem.pdf) – Tinus

+2

@Mitch: la mayoría de las veces, es suficiente colocar datos en el sistema de archivos y dejar que las pruebas unitarias siguen su curso. Sin embargo, me he encontrado con métodos que ejecutan muchas operaciones de IO, y la configuración del entorno de prueba para tales métodos se simplifica enormemente al usar un sistema de archivos simulado. –

Respuesta

107

Editar: Instale el paquete NuGet System.IO.Abstractions.

Este paquete no existía cuando se aceptó originalmente esta respuesta. Se proporciona la respuesta original de contexto histórico a continuación:

Usted puede hacerlo mediante la creación de una interfaz:

interface IFileSystem { 
    bool FileExists(string fileName); 
    DateTime GetCreationDate(string fileName); 
} 

y la creación de una aplicación 'real' que utiliza System.IO.File.Exists (). Luego puede simular esta interfaz utilizando un marco de burla ; Recomiendo Moq.

Edit: alguien ha hecho esto y amablemente lo publicó en línea here.

He usado este enfoque para burlarse cabo DateTime.UtcNow en una interfaz Iclock (muy, muy útil para nuestras pruebas para poder controlar el flujo del tiempo!), Y más tradicionalmente, una interfaz ISqlDataAccess .

Otro enfoque podría ser utilizar TypeMock, esto le permite interceptar llamadas a clases y anularlas. Sin embargo, esto cuesta dinero, y tendría que ser instalado en las computadoras de todo su equipo y su servidor de compilación para ejecutar, también, aparentemente no funcionará para System.IO.Archivo, como es can't stub mscorlib.

También podría simplemente aceptar que ciertos métodos no se pueden probar por unidad en y probarlos en un paquete de pruebas de integración/sistema de ejecución lenta independiente suite.

+1

En mi opinión, crear una interfaz como Matt describe aquí es el camino a seguir. Incluso he escrito una herramienta que genera tales interfaces para usted, que es útil cuando se trata de simular clases estáticas y/o selladas, o métodos que no son deterministas (es decir, relojes y generadores de números aleatorios). Consulte http://jolt.codeplex.com para obtener más información. –

+1

Véase también http://systemwrapper.codeplex.com/ – TrueWill

+2

Mejor biblioteca que he usado para este http://systemioabstractions.codeplex.com/ –

1

No estoy seguro de cómo se burlaría del sistema de archivos. Lo que podría hacer es escribir una configuración de dispositivo de prueba que cree una carpeta, etc. con la estructura necesaria para las pruebas. Un método de desmontaje lo limpiaría después de que se ejecuten las pruebas.

Editado para agregar: Al pensar en esto un poco más, no creo que quiera burlarse del sistema de archivos para probar este tipo de métodos. Si se burla del sistema de archivos para que sea verdadero si existe un archivo determinado y lo usa en su prueba de un método que verifica si ese archivo existe, entonces no está probando mucho de nada. Donde sería útil burlarse del sistema de archivos es si desea probar un método que tenía una dependencia en el sistema de archivos, pero la actividad del sistema de archivos no era parte integral del método bajo prueba.

10

Probablemente va a tener que crear un contrato para definir qué cosas necesita del sistema de archivos y luego escribir un envoltorio alrededor de esas funcionalidades. En ese punto, podrá burlarse o anular la implementación.

Ejemplo:

interface IFileWrapper { bool Exists(String filePath); } 

class FileWrapper: IFileWrapper 
{ 
    bool Exists(String filePath) { return File.Exists(filePath); }   
} 

class FileWrapperStub: IFileWrapper 
{ 
    bool Exists(String filePath) 
    { return (filePath == @"C:\myfilerocks.txt"); } 
} 
1

Sería difícil de burlar el sistema de archivos en una prueba, ya que las API de archivos .NET no se basa realmente en las interfaces o clases extensibles que podrían ser burlado.

Sin embargo, si tiene su propia capa funcional para acceder al sistema de archivos, puede simular eso en una prueba unitaria.

Como alternativa a la burla, considere la posibilidad de crear las carpetas y archivos que necesita como parte de su configuración de prueba, y eliminarlos en su método de desmontaje.

-1

Me gustaría ir con la respuesta de Jamie Ide. No trates de burlarse de cosas que no escribiste. Habrá todo tipo de dependencias que no conocía - clases selladas, métodos no virtuales, etc.

Otro enfoque sería envolver los métodos de consulta con algo que sea burlable. p.ej. crea una clase llamada FileWrapper que permita el acceso a los métodos de archivo, pero es algo que puedes simular.

1

Para responder a su pregunta específica: No, no hay bibliotecas que le permitan simular las llamadas de E/S de archivos (que yo sepa). Esto significa que las pruebas unitarias "correctamente" de sus tipos requerirán que tenga en cuenta esta restricción cuando defina sus tipos.

Nota rápida acerca de cómo defino una prueba de unidad "adecuada". Creo que las pruebas unitarias deben confirmar que se obtiene el resultado esperado (ya sea una excepción, llamada en un método, etc.) con entradas conocidas. Esto le permite configurar las condiciones de prueba de su unidad como un conjunto de entradas y/o estados de entrada. La mejor manera que he encontrado para hacerlo es usar servicios basados ​​en interfaz e inyección de dependencia para que cada responsabilidad externa a un tipo se proporcione a través de una interfaz pasada a través de un constructor o propiedad.

Por lo tanto, con esto en mente, volvamos a su pregunta. Me he burlado de las llamadas al sistema de archivos creando una interfaz IFileSystemService junto con una implementación FileSystemService que es simplemente una fachada sobre los métodos del sistema de archivos mscorlib. Mi código luego usa el IFileSystemService en lugar de los tipos mscorlib. Esto me permite conectar mi estándar FileSystemService cuando la aplicación se está ejecutando o simular el IFileSystemService en mis pruebas unitarias. El código de la aplicación es el mismo independientemente de cómo se ejecute, pero la infraestructura subyacente permite que ese código se pruebe fácilmente.

Reconozco que es un dolor utilizar el envoltorio alrededor de los objetos del sistema de archivos mscorlib pero, en estos escenarios específicos, vale la pena el trabajo adicional ya que las pruebas se vuelven mucho más fáciles y confiables.

-1

Actualmente utilizamos un motor de datos propietario y su API no está expuesta como interfaces, por lo que apenas podemos probar nuestras unidades con nuestro código de acceso a datos. Luego fui con el enfoque de Matt y Joseph también.

1

Crear una interfaz y burlarse de ella para realizar pruebas es la forma más sencilla de hacerlo. Sin embargo, como alternativa, podría echarle un vistazo al marco Microsoft Moles.

3

Me he encontrado con las siguientes soluciones para esto:

  • pruebas de escritura de integración, no pruebas unitarias. Para que esto funcione, necesitas una forma simple de crear una carpeta donde puedas volcar cosas sin preocuparte de que otras pruebas interfieran. Tengo una clase simple TestFolder que puede crear una carpeta única por método de prueba para usar.
  • Escriba un System.IO.File fallable. Eso es crear un IFile.cs. Me parece que usar esto a menudo termina con pruebas que simplemente prueban que se pueden escribir declaraciones de burla, pero se usan cuando el uso de IO es pequeño.
  • Examine su capa de abstracción y extraiga el archivo IO de la clase. Crea una interfaz para esto. El resto usa pruebas de integración (pero esto será muy pequeño).Esto difiere de arriba en que en lugar de archivo. Lea escribir el intento, por ejemplo ioThingie.loadSettings()
  • System.IO.Abstractions. No he usado esto todavía, pero es con el que estoy más emocionado por jugar.

Termino usando todos los métodos anteriores, dependiendo de lo que estoy escribiendo. Pero la mayor parte del tiempo termino pensando que la abstracción es incorrecta cuando escribo pruebas unitarias que afectan al IO. existe

+2

El enlace a IFile.cs está roto. –

70

Install-Package System.IO.Abstractions

Esta biblioteca imaginaria ahora, hay un paquete para NuGet System.IO.Abstractions, que abstrae el espacio de nombres System.IO.

También hay un conjunto de ayudantes de prueba, System.IO.Abstractions.TestingHelpers que, en el momento de redactar este documento, está implementado solo parcialmente, pero es un muy buen punto de partida.

+3

Creo que la estandarización en torno a esta abstracción ya construida es la mejor opción. Nunca he oído hablar de esa biblioteca, así que muchas gracias por la información. – julealgon

5

Mi recomendación es usar http://systemwrapper.codeplex.com/ ya que proporciona envoltorios para los tipos más utilizados en espacio de nombres System

+0

Actualmente estoy usando esta biblioteca, y ahora que he descubierto que sus abstracciones para cosas como FileStream no incluyen IDisposable, estoy buscando un reemplazo. Si la biblioteca no me permite desechar adecuadamente las transmisiones, no puedo recomendarla (ni usarla) para manejar ese tipo de operaciones. –

+0

IFileStreamWrap de SystemWrapper implementa IDisposable ahora. – tster

0

solución común es el uso de algunos API del sistema de archivos abstracta (como Apache Commons VFS para Java): toda la lógica de aplicación utiliza la API y prueba de unidad es capaz de burlarse del sistema de archivos real con la implementación del stub (emulación en memoria o algo así).

Para C# existe API similar: NI.Vfs que es muy similar a Apache VFS V1. Contiene implementaciones predeterminadas tanto para el sistema de archivos local como para el sistema de archivos en la memoria (el último se puede usar en pruebas unitarias desde el recuadro).

0

Puede hacerlo utilizando Microsoft Fakes sin la necesidad de cambiar su base de código, por ejemplo, porque ya se ha congelado.

Primera generate a fake assembly para System.dll - o cualquier otro paquete y luego se burlan de los rendimientos esperados como en:

using Microsoft.QualityTools.Testing.Fakes; 
... 
using (ShimsContext.Create()) 
{ 
    System.IO.Fakes.ShimFile.ExistsString = (p) => true; 
    System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content"; 

     //Your methods to test 
} 
Cuestiones relacionadas