2012-02-15 10 views
5

Pila de tecnología: .NET 4, C#, NUnitTDD: ¿Es plausible tener pruebas de integración, pero no pruebas de unidades?

Estoy intentando aplicar el desarrollo impulsado por prueba a un nuevo proyecto que realiza el procesamiento de imágenes. Tengo una clase base que contiene métodos y subclases de E/S de archivos compartidos que realizan varios algoritmos de procesamiento específicos. Según tengo entendido, las pruebas unitarias no tocan el sistema de archivos u otros objetos, y se burlan del comportamiento donde eso ocurre. Mi clase base solo contiene accesos simples y llamadas directas de E/S del sistema de archivos.

public class BaseFile 
{ 
    public String Path { get; set; } 

    public BaseFile() 
    { 
     Path = String.Empty; 
    } 

    public BaseFile(String path) 
    { 
     if (!File.Exists(path)) 
     { 
      throw new FileNotFoundException("File not found.", path); 
     } 

     Path = path; 
    } 
} 

¿Hay algún valor en probar estos métodos? Si es así, ¿cómo podría abstraer las llamadas al sistema de archivos?

Mi otra pregunta es cómo probar la subclase que es específica para un tipo de archivo de imagen (~ 200 MB). He buscado en el sitio y encontré similarquestions, pero ninguno tiene que ver con los tamaños de archivo con los que estoy trabajando en este proyecto. ¿Es plausible que una clase tenga pruebas de integración (usando un "archivo dorado"), pero no hay pruebas de unidad? ¿Cómo podría seguir estrictamente los métodos TDD y escribir primero una prueba fallida en este caso?

+4

Si TDD es difícil de aplicar, o inadecuado, no lo aplique. No es una bala de plata. – CharlesB

+1

@CharlesB, estoy de acuerdo. Lamentablemente, ese sentimiento se usa a menudo como excusa para no usar TDD cuando es correcto y ventajoso hacerlo. A veces hay una gran curva o mucho trabajo preliminar pero esto generalmente vale la pena. –

+1

@CharlesB No estoy seguro de que estoy de acuerdo con eso. El problema es que esta clase que hace un poco de trabajo no se prueba y, por lo tanto, su confianza para cambiarla disminuye. El problema es que no es fácil burlarse de los métodos estáticos en System.IO, pero eso no significa que renuncies a las pruebas, solo significa que necesitas hacer un poco más de trabajo para que sean comprobables. Esta es la razón por la cual MS presenta System.Web.HttpContextBase, para abordar los problemas al no poder simular fácilmente un HttpContext. No necesitamos probar httpContext, solo que nuestro código interactúa correctamente con él. – Andy

Respuesta

4

En respuesta a su primera pregunta, sí, hay un valor en probar estos métodos. He publicado una biblioteca que facilita hacer exactamente eso sin llegar al sistema de archivos: https://bitbucket.org/mylesmcdonnell/mpm.io/wiki/Home

En (no es) respuesta a su segunda pregunta, necesitaría ver algún código, pero sospecho que es posible que deba tomar un código similar acercamiento a la lib anterior. a saber; definir la interfaz, definir proxy para concreto, definir fábrica para devolver proxy o simulacro.

+0

Gracias por publicar este código fuente. Estoy marcando tu publicación como la respuesta porque proporcionaste la mejor solución general. – Noren

3

¿Cómo podría seguir estrictamente los métodos de TDD y escribir primero una prueba de falla en este caso?

Fácil! Se burla del sistema de archivos :)

Esto puede parecer mucho trabajo, pero por lo general solo necesita implementar algunos métodos y ampliarlos según sea necesario. En el caso anterior ... solo necesitas uno.

public interface IFileStore 
{ 
    Boolean FileExists(String path); 
} 

La idea es delegar su trabajo de archivos detrás de la interfaz, y crear una aplicación concreta para hacer el trabajo pesado. Básicamente un Adapter pattern.

Ni siquiera me opongo a "DI del pobre" para este tipo de cosas, ya que puede implementar un contenedor más adelante si su aplicación lo requiere. En este caso ... probablemente siempre utilizará el sistema de archivos real.

public class BaseFile 
{ 
    private IFileStore _fileStore 
    public IFileStore FileStore 
    { 
     get 
     { 
      return _fileStore ?? (_fileStore = new ConcreteFileStore()); 
     } 
     set 
     { 
      _fileStore = value; 
     } 
    } 

    //SNIP... 
} 

Ahora tiene una implementación comprobable, y no tendrá que depender de ningún archivo "Golden".

+0

Gracias por su publicación. Por ahora, voy a implementar su solución hasta que necesite más llamadas a System.IO, y en ese momento usaré el código de Myles. – Noren

0

Las pruebas de integración tienen valor por sí mismas. Si se burla del sistema de archivos, como se explica en la respuesta de Josh, realmente no está seguro de que su código se ejecute realmente en producción. El sistema de archivos tiene muchos contratos ocultos que no son triviales para burlarse. Si su simulacro/falso muestra un comportamiento ligeramente diferente, su código podría comenzar a confiar en él sin que usted lo sepa.

Solo una prueba de integración puede dar ciertas certezas. (¡Las pruebas de integración también tienen desventajas!).

+0

Correcto. No estoy insinuando que * no deberías * hacer la prueba de integración. Más bien, ese TDD todavía se puede hacer incluso en cosas que normalmente no consideramos como Sistemas de archivos. – Josh

+0

No quise decir devaluar tu explicación. Hice referencia porque era bueno. – usr

1

Hay valor en la prueba de estos métodos

Si bien puede parecer como un trabajo extra para el aumento trivial en este momento, añadiendo pruebas como BaseFile debe tirar FileNotFoundException cuando no existe el archivo lleva a cabo al menos dos goles :

definir una lista de comportamientos esperados

los recién llegados al proyecto pueden revisar los nombres de las pruebas para determinar la forma en que su c las chicas están destinadas a operar. Ellos sabrán qué esperar en cada situación: una excepción, un resultado nulo, por defecto, etc.

También lo obliga a pensar y definir en inglés cómo quiere que las cosas funcionen, en lugar de solo arrojando condiciones y excepciones aquí y allá. Esto debería dar como resultado una filosofía muy consistente aplicada en todo su proyecto.

crecer un conjunto de pruebas de regresión automatizadas

Considere que alguien ve algún código de lanzar una excepción en una condición particular, pero creen que es más sabia que hacer algo más (comer el error, además de añadir un nuevo Propiedad IsValid para que los consumidores puedan saber si la construcción/inicialización fue exitosa). Si hacen tal cambio, la prueba llamará la atención con rapidez sobre el cambio. Hubo una decisión consciente e intencional detrás de cómo eran las cosas, y las personas podrían haber llegado a confiar en el comportamiento existente: este cambio necesita más discusión antes de poder ser aceptado.

En cuanto a la segunda parte de su pregunta, creo que Josh y Myles ya han proporcionado buenos consejos.

+0

Has hecho buenos puntos. Creo que me iré en las pruebas de estos métodos simples. – Noren

1

Burlarse de llamadas simples al sistema de archivos usando interfaces parece una exageración. Lo mismo ocurre con cosas como burlarse de la hora actual usando ITimeService. Yo tiendo a usar Func o acción, ya que es mucho más sencillo:

public static Func<string, bool> FileExists = System.IO.File.Exists; 
public static Func<DateTime> GetCurrentTime =() => DateTime.Now; 

Desde esos son públicas puedo burlarse de la facilidad en las pruebas unitarias. El código se mantiene simple, no es necesario inyectar varias interfaces para cosas simples. Para las transmisiones usualmente uso MemoryStream en unidades tets.

Cuestiones relacionadas