¿Cómo se burla del sistema de archivos en C # para las pruebas unitarias?

¿Hay alguna biblioteca o método 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.

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

Este paquete no existía cuando esta respuesta fue originalmente aceptada. La respuesta original se proporciona para el contexto histórico a continuación:

Puedes hacerlo creando una interfaz:

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

y crear una implementación ‘real’ que use System.IO.File.Exists () etc. Luego puede simular esta interfaz usando un marco de burla; Recomiendo Moq .

Editar: alguien ha hecho esto y amablemente lo publicó en línea aquí .

Utilicé este enfoque para simular DateTime.UtcNow en una interfaz de IClock (realmente muy útil para que nuestras pruebas puedan controlar el flujo de tiempo) y, más tradicionalmente, una interfaz ISqlDataAccess.

Otro enfoque podría ser usar TypeMock , esto le permite interceptar llamadas a clases y archivarlas. Sin embargo, esto cuesta dinero, y tendría que instalarse en las computadoras de todo tu equipo y en el servidor de comstackción para poder ejecutarse; además, aparentemente no funcionará para System.IO.File, ya que no es compatible con mscorlib .

También puede aceptar que ciertos métodos no se pueden probar por unidad y probarlos en un conjunto de pruebas de integración / sistema de ejecución lenta independiente.

Install-Package System.IO.Abstractions

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

También hay un conjunto de asistentes de prueba, System.IO.Abstractions.TestingHelpers que, en el momento de redactar este informe, solo se implementa parcialmente, pero es un muy buen punto de partida.

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"); } } 

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

He encontrado las siguientes soluciones a esto:

  • Escribir pruebas 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.
  • Escribe 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 rest usa pruebas de integración (pero esto será muy pequeño). Esto difiere de arriba en que en lugar de archivo. Lea escribir el bash, digamos 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.

Sería difícil burlarse del sistema de archivos en una prueba, ya que las API de archivos .NET no están realmente basadas en interfaces o clases extensibles que puedan ser objeto de burla.

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 desassembly.

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 desassembly 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.

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 sobre cómo definir 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 dependency injection 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.

Entonces, 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 servicio IFileSystemService lugar de los tipos mscorlib. Esto me permite conectar mi FileSystemService estándar cuando la aplicación se está ejecutando o IFileSystemService 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 usar 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.

Crear una interfaz y burlarse de ella para probarla es la manera más limpia de hacerlo. Sin embargo, como alternativa, podría echar un vistazo al marco de Microsoft Moles .

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

Primero genere un ensamblaje falso para System.dll – o cualquier otro paquete y luego simule las devoluciones esperadas 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 } 

La solución común es usar una API abstracta del sistema de archivos (como Apache Commons VFS para Java): toda la lógica de la aplicación usa API y la prueba unitaria es capaz de simular un sistema de archivos real con implementación de stub (emulación en memoria o algo así).

Para C # existe la 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).

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 appopiate con algo que sea burlable. por ejemplo, crea una clase llamada FileWrapper que permite el acceso a los métodos de archivo pero es algo que puedes simular.

Actualmente usamos 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.

Intereting Posts