c# - microsoft - visual studio installer
Unidad de prueba de archivos E/S (5)
Actualmente, consumo un objeto IFileSystem a través de la inyección de dependencia. Para el código de producción, una clase contenedora implementa la interfaz, envolviendo las funciones específicas de IO que necesito. Al probar, puedo crear una implementación nula o de stub y proporcionar eso a la clase bajo prueba. La clase probada no es la más sabia.
Leyendo los subprocesos relacionados con las pruebas de unidad existentes aquí en Stack Overflow, no pude encontrar uno con una respuesta clara acerca de cómo probar las operaciones de E / S del archivo de prueba. Recientemente comencé a investigar las pruebas unitarias, al haber tenido conocimiento de las ventajas pero tener dificultades para acostumbrarme a escribir pruebas primero. He configurado mi proyecto para usar NUnit y Rhino Mocks, y aunque entiendo el concepto detrás de ellos, tengo problemas para entender cómo usar Mock Objects.
Específicamente, tengo dos preguntas que me gustaría responder. Primero, ¿cuál es la forma correcta de analizar las operaciones de E / S del archivo de prueba? En segundo lugar, en mis intentos por aprender acerca de las pruebas unitarias, me encontré con la inyección de dependencia. Después de configurar y poner en funcionamiento el Ninject, me preguntaba si debería usar DI en mis pruebas unitarias, o simplemente crear instancias de objetos directamente.
Consulte Tutorial para TDD usando Rhino SystemWrapper y SystemWrapper .
SystemWrapper ajusta muchas de las clases de System.IO incluyendo File, FileInfo, Directory, DirectoryInfo, .... Puedes ver la lista completa .
En este tutorial estoy mostrando cómo hacer pruebas con MbUnit, pero es exactamente lo mismo para NUnit.
Tu prueba se verá más o menos así:
[Test]
public void When_try_to_create_directory_that_already_exists_return_false()
{
var directoryInfoStub = MockRepository.GenerateStub<IDirectoryInfoWrap>();
directoryInfoStub.Stub(x => x.Exists).Return(true);
Assert.AreEqual(false, new DirectoryInfoSample().TryToCreateDirectory(directoryInfoStub));
directoryInfoStub.AssertWasNotCalled(x => x.Create());
}
Desde 2012, puede hacerlo utilizando 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
}
No hay necesariamente una cosa que hacer al probar el sistema de archivos. En verdad, hay varias cosas que podrías hacer, dependiendo de las circunstancias.
La pregunta que debes hacer es: ¿Qué estoy probando?
¿Que el sistema de archivos funciona? Probablemente no necesite probarlo a menos que esté usando un sistema operativo con el que no esté familiarizado. Entonces, si simplemente está dando un comando para guardar archivos, por ejemplo, es una pérdida de tiempo escribir una prueba para asegurarse de que realmente se guarde.
Que los archivos se guardan en el lugar correcto? Bueno, ¿cómo sabes cuál es el lugar correcto? Es de suponer que tiene un código que combina una ruta con un nombre de archivo. Este es un código que puede probar fácilmente: su entrada es de dos cadenas y su salida debe ser una cadena que es una ubicación de archivo válida construida con esas dos cadenas.
¿Que obtienes el conjunto correcto de archivos de un directorio? Probablemente tengas que escribir una prueba para tu clase get-file que realmente prueba el sistema de archivos. Pero debe usar un directorio de prueba con archivos que no cambien. También debe colocar esta prueba en un proyecto de prueba de integración, porque esta no es una verdadera prueba de unidad, porque depende del sistema de archivos.
Pero, necesito hacer algo con los archivos que obtengo. Para esa prueba, debes usar un falso para tu clase get-file. Su falso debería devolver una lista de archivos codificados. Si usa un realizador de archivos real y un procesador de archivos real , no sabrá cuál causa un error en la prueba. Por lo tanto, su clase de procesador de archivos, en pruebas, debe hacer uso de una clase falsa de búsqueda de archivos. Su clase de procesador de archivos debe tomar la interfaz de obtención de archivos. En el código real, pasarás el verdadero captador de archivos. En el código de prueba, pasará un compilador de archivos falso que devuelve una lista estática conocida.
Los principios fundamentales son:
- Use un sistema de archivos falso, escondido detrás de una interfaz, cuando no está probando el sistema de archivos.
- Si necesita probar operaciones reales de archivos, entonces
- marque la prueba como una prueba de integración, no como una prueba unitaria.
- tener un directorio de prueba designado, un conjunto de archivos, etc. que siempre estará allí en un estado sin cambios, por lo que sus pruebas de integración orientadas a archivos pueden pasar constantemente.
Q1:
Tienes tres opciones aquí.
Opción 1: vivir con eso.
(sin ejemplo: P)
Opción 2: crea una ligera abstracción cuando sea necesario.
En lugar de hacer las E / S de archivo (File.ReadAllBytes o lo que sea) en el método bajo prueba, puede cambiarlo para que el IO se haga afuera y se pase una transmisión en su lugar.
public class MyClassThatOpensFiles
{
public bool IsDataValid(string filename)
{
var filebytes = File.ReadAllBytes(filename);
DoSomethingWithFile(fileBytes);
}
}
se convertiría
// File IO is done outside prior to this call, so in the level
// above the caller would open a file and pass in the stream
public class MyClassThatNoLongerOpensFiles
{
public bool IsDataValid(Stream stream) // or byte[]
{
DoSomethingWithStreamInstead(stream); // can be a memorystream in tests
}
}
Este enfoque es una compensación. En primer lugar, sí, es más comprobable. Sin embargo, intercambia la capacidad de prueba por una pequeña adición a la complejidad. Esto puede afectar la capacidad de mantenimiento y la cantidad de código que tiene que escribir, además de que puede mover su problema de prueba a un nivel.
Sin embargo, en mi experiencia, este es un enfoque agradable y equilibrado, ya que puede generalizar y hacer comprobable la importante lógica sin comprometerse con un sistema de archivos totalmente integrado. Es decir, puede generalizar las partes que realmente le interesan, dejando el resto tal como está.
Opción 3: ajustar todo el sistema de archivos
Yendo un paso más allá, burlarse del sistema de archivos puede ser un enfoque válido; depende de la cantidad de hinchazón con la que estés dispuesto a vivir.
He ido esta ruta antes; Tenía una implementación de sistema de archivos envuelto, pero al final simplemente lo eliminé. Hubo diferencias sutiles en la API, tuve que inyectarlo en todas partes y, en última instancia, fue un dolor extra con pocas ganancias ya que muchas de las clases que lo usaban no eran muy importantes para mí. Si hubiera estado usando un contenedor IoC o escribiendo algo que fuera crítico y las pruebas necesitaran ser rápidas, podría haberme quedado con él. Al igual que con todas estas opciones, su millaje puede variar.
En cuanto a su pregunta sobre el contenido de IoC:
Inyecta tus dobles de prueba manualmente. Si tiene que hacer un montón de trabajo repetitivo, solo use los métodos de instalación / fábrica en sus pruebas. ¡Utilizar un contenedor de IoC para realizar pruebas sería excesivo en extremo! Sin embargo, tal vez no entiendo tu segunda pregunta.