Pregunta

Por ejemplo, estoy escribiendo pruebas contra un CsvReader. Es una clase simple que enumera y divide filas de texto. Su única razón de ser es ignorar comas entre comillas. Es menos que una página.

Por " caja negra " probando la clase, he comprobado cosas como

  • ¿Qué pasa si el archivo no existe?
  • ¿Qué pasa si no tengo permiso en el archivo?
  • ¿Qué sucede si el archivo no tiene saltos de línea de Windows?

Pero, de hecho, todas estas cosas son el negocio de StreamReader. Mi clase trabaja sin hacer nada en estos casos. Así que, en esencia, mis pruebas detectan los errores generados por StreamReader y las conductas de prueba manejadas por el marco. Se siente como mucho trabajo para nada.

He visto las preguntas relacionadas

Mi pregunta es, ¿me falta el punto de " caja de vidrio " probando si uso lo que sé para evitar este tipo de trabajo?

¿Fue útil?

Solución

Esto realmente depende de la interfaz de su CsvReader, debe considerar lo que espera el usuario de la clase.

Por ejemplo, si uno de los parámetros es un nombre de archivo y el archivo no existe, ¿qué debería suceder? Esto no debe depender de si utiliza un lector de flujo o no. Las pruebas unitarias deben probar el comportamiento externo observable de su clase y, en algunos casos, profundizar un poco más y además garantizar que se cubren ciertos detalles de implementación, por ejemplo. el archivo se cierra cuando el lector ha terminado.

Sin embargo, no desea que las Pruebas Unitarias dependan de todos los detalles o que asuman que, debido a un detalle de implementación, algo sucederá.

Todos los ejemplos que menciona en su pregunta implican un comportamiento observable (en este caso, circunstancias excepcionales) de su clase y, por lo tanto, deben realizarse pruebas unitarias relacionadas con ellos.

Otros consejos

No creo que debas perder el tiempo probando cosas que no son tu código. Es una opción de diseño, no una opción de prueba, ya sea para manejar los errores del marco subyacente o permitir que se propaguen hasta la persona que llama. FWIW, creo que tienes razón al dejar que se propaguen. Sin embargo, una vez que haya tomado la decisión de diseño, su prueba de unidad debe cubrir su código (y cubrirlo bien) sin probar el marco subyacente. Usar inyección de dependencia y un simulacro de Stream también es una buena idea, también.

[EDITAR] Ejemplo de inyección de dependencia (vea el enlace arriba para más información)

Al no usar la inyección de dependencia tenemos:

public class CvsReader {
   private string filename;
   public CvsReader(string filename)
   {
      this.filename = filename;
   }

   public string Read()
   {
      StreamReader reader = new StreamReader( this.filename );
      string contents = reader.ReadToEnd();
      .... do some stuff with contents...
      return contents;
   }
}

Con la inyección de dependencia (inyección de constructor) hacemos:

public class CvsReader {
   private IStream stream;
   public CvsReader( IStream stream )
   {
      this.stream = stream;
   }

   public string Read()
   {
       StreamReader reader = new StreamReader( this.stream );
       string contents = reader.ReadToEnd();
       ...  do some stuff with contents ...
       return contents;
   }
}

Esto permite que el CvsReader sea más fácil de probar. Pasamos una instancia implementando la interfaz de la que dependemos en el constructor, en este caso IStream. Debido a esto, podemos crear otra clase (tal vez una clase simulada) que implementa IStream, pero no necesariamente hace I / O de archivos. Podemos usar esta clase para alimentar a nuestro lector cualquier información que queramos sin involucrar ninguno de los marcos subyacentes. En este caso, usaría un MemoryStream ya que acabamos de leer. Sin embargo, en el caso de que quisiéramos, podríamos usar una clase simulada y darle una interfaz más rica que permita a nuestras pruebas configurar las respuestas que brinda. De esta manera podemos probar el código que escribimos y no involucrar en absoluto el código del marco subyacente. Alternativamente, también podríamos pasar un TextReader, pero el patrón de inyección de dependencia normal utiliza interfaces y quería mostrar el patrón con interfaces. Podría decirse que pasar un TextReader sería mejor ya que el código anterior aún depende de la implementación de StreamReader.

Sí, pero eso sería estrictamente a los fines de las pruebas unitarias:

Puede abstraer la implementación de su lector CSV de cualquier StreamReader en particular definiendo una interfaz de lector de flujo abstracto y probando su propia implementación con un lector de flujo simulado que implementa esa interfaz. Su lector simulado obviamente sería inmune a errores como archivos no existentes, problemas de permisos, diferencias de sistema operativo, etc. Por lo tanto, estaría probando su propio código por completo y podría lograr una cobertura del 100% del código.

Tiendo a estar de acuerdo con tvanfosson: si heredas de un StreamReader y lo extiendes de alguna manera, tus pruebas unitarias solo deben ejercer la funcionalidad que has agregado o modificado. De lo contrario, perderá mucho tiempo y energía cognitiva escribiendo, leyendo y manteniendo pruebas que no agregan ningún valor.

Aunque markj tiene razón, las pruebas deben cubrir el " comportamiento externo observable " de una clase, creo que es apropiado considerar de dónde proviene ese comportamiento. Si se trata de un comportamiento por herencia de otra clase (presumiblemente probada), entonces no veo ningún beneficio al agregar sus propias pruebas de unidad . OTOH, si se trata de un comportamiento a través de la composición, podría justificar algunas pruebas para asegurarse de que los pasos a través funcionan correctamente.

Mi preferencia sería probar por unidad la funcionalidad específica que modificas, y luego escribir pruebas de integración que comprueben las condiciones de error, pero en el contexto de la necesidad de la empresa que finalmente apoyas.

Solo un FYI, si esto es .NET, debes considerar no reinventar la rueda.

Para C #

Agregar una referencia a Microsoft.VisualBasic Utilice la clase fantástica Microsoft.VisualBasic.FileIO.TextFieldParser () para manejar sus necesidades de análisis de CSV.

Microsoft ya lo probó, por lo que no tendrás que hacerlo.

Disfruta.

Siempre debe estar administrando los errores que lanza su marco; de esa manera su aplicación es robusta & amp; no se estrella en errores catastróficos ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top