2011-08-16 23 views
5

Tengo esta firma de método: List<ITMData> Parse(string[] lines)Cómo probar de manera efectiva un analizador de archivos planos de longitud fija utilizando MSpec?

ITMData tiene 35 propiedades.

¿Cómo podría efectivamente probar tal analizador?

Preguntas:

  • ¿Debo cargar el archivo completo (¿Puedo usar System.IO)?
  • ¿Debo poner una línea del archivo en una constante de cadena?
  • ¿Debo probar una o más líneas
  • ¿Debo probar cada propiedad de ITMData o debería probar el objeto completo?
  • ¿Qué pasa con el nombramiento de mi prueba?

EDITAR

me cambió la firma del método a ITMData Parse(string line).

Código de prueba:

[Subject(typeof(ITMFileParser))] 
public class When_parsing_from_index_59_to_79 
{ 
    private const string Line = "........."; 
    private static ITMFileParser _parser; 
    private static ITMData _data; 

    private Establish context =() => { _parser = new ITMFileParser(); }; 

    private Because of =() => { _data = _parser.Parse(Line); }; 

    private It should_get_fldName =() => _data.FldName.ShouldBeEqualIgnoringCase("HUMMELDUMM"); 
} 

EDIT 2

todavía no estoy seguro de si debo probar únicamente una propiedad por clase. En mi opinión, esto me permite dar más información para la especificación, concretamente que cuando analizo una sola línea del índice 59 al índice 79 obtengo fldName. Si pruebo todas las propiedades dentro de una clase I pierda esta información. ¿Estoy sobreespecificando mis pruebas?

mis pruebas ahora se ve así:

[Subject(typeof(ITMFileParser))] 
public class When_parsing_single_line_from_ITM_file 
{ 
    const string Line = "" 

    static ITMFileParser _parser; 
    static ITMData _data; 

    Establish context =() => { _parser = new ITMFileParser(); }; 

    private Because of =() => { _data = _parser.Parse(Line); }; 

    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    It should_get_fld??? =() => _data.Fld???.ShouldEqual(???); 
    ... 

} 
+0

¿Qué quiere decir con "eficacia" exactamente? ¿Desea minimizar el tiempo de desarrollo de las pruebas unitarias? –

+0

bien, es decir, ¿está probando con una cuerda o con muchas cuerdas? probando con una cadena que contiene solo los valores que quiero afirmar o tomar toda la cadena. – Rookian

+0

Todavía no estoy 100% seguro de cuál es exactamente la pregunta, pero crearía un conjunto de diferentes pruebas de unidad. Un método de prueba puede pasar en una única cadena específica y afirmar que se ha analizado correctamente en las propiedades relevantes en el objeto IMTData resultante. Quizás otra prueba que pruebe que, dadas 20 líneas, produzca una Lista de 20 elementos. Es posible que tenga otros métodos para casos difíciles específicos. Esto es más o menos lo que describió @Kenny, cosas básicas para probar la unidad. Tome los requisitos para este método de Parse y realice una ingeniería inversa en algunos casos de prueba que demuestren la corrección. –

Respuesta

2

Esto es lo que haría normalmente si estoy frente a un problema tan:

Un descargo de responsabilidad corta con antelación: Creo que lo más bajar por la "prueba de integración" o "probar el analizador en su conjunto" ruta en lugar de probar líneas individuales. En el pasado, más de una vez me he enfrentado a la situación en la que se filtraron muchos detalles de implementación en mis pruebas y me forzaron a cambiar las pruebas a menudo cuando cambié los detalles de la implementación. Caso típico de sobreespecificación supongo; -/

  1. No incluiría la carga de archivos en el analizador. Como @mquander sugirió que preferiría usar un TextReader o un IEnumerable como parámetro de entrada. Esto dará lugar a pruebas mucho más rápidas, ya que puede especificar la entrada del analizador en la memoria y no tiene que tocar el sistema de archivos.
  2. No soy un gran admirador de los datos de prueba manuales, por lo que en la mayoría de los casos estoy usando recursos integrados y ResourceManager para cargar datos de prueba directamente desde el ensamblado de especificación mediante assembly.GetManifestResource(). Por lo general, tengo un montón de métodos de extensión en mi solución para agilizar la lectura de los recursos (algo así como TextReader TextResource.Load ("NAME_OF_SOME_RESOURCE")).
  3. En cuanto a MSpec: estoy usando una clase por archivo para analizar. Para cada propiedad que se prueba en el resultado analizado, tengo una aserción independiente (It). Normalmente, estos son revestimientos, por lo que la cantidad adicional de codificación no es tan grande.En términos de documentación y diagnóstico, es una gran ventaja ya que cuando una propiedad no se analiza correctamente, puede ver directamente qué afirmación falló sin tener que buscar en la fuente o buscar números de línea. También aparece en su archivo de resultados MSpec. Además, no oculta otras afirmaciones fallidas (la situación en la que corrige una afirmación solo para ver que la especificación falla en la siguiente línea con la siguiente afirmación). Esto, por supuesto, te obliga a pensar más sobre la redacción que usas en tus especificaciones, pero para mí también es una gran ventaja ya que soy un defensor de la idea de que el lenguaje forma pensamiento. En otras palabras, si no tiene ni idea de cómo darle nombre a su afirmación, probablemente haya algo raro sobre su especificación o su implementación.
  4. En cuanto a su firma de método para el analizador: No devolvería un tipo concreto como List T > o una matriz y también sugeriría no devolver la lista mutable < tipo 0 >. Lo que básicamente estás diciendo aquí es: "Oye, puedes analizar el resultado del análisis después de que haya terminado", que en la mayoría de los casos es probablemente lo que no quieres. Yo sugeriría que volver IEnumerable <T> lugar (o ICollection <T> si realmente se necesita para modificarla después)
+0

gracias hasta ahora :) 1. Analizo una sola línea, porque cada línea se analiza de la misma manera 2. Me preocuparé por poner mis datos de prueba en los recursos. 3. No puedo usar una clase por tipo de archivo, porque hay alguna lógica especial que depende de la configuración. Por lo tanto, algunas de las propiedades deben probarse por separado. 4. Elegí analizar solo una línea. – Rookian

1

normalmente trato de tener en cuenta el éxito común y dejar los escenarios, junto con casos extremos. Los requisitos también son útiles para configurar casos de uso apropiados. Considere Pex para enumerar varios escenarios.

4

¿Debo cargar todo el archivo (puedo usar System.IO)?

Si hace esto, ya no es una prueba de unidad, se convierte en una prueba de integración o de regresión. Puede hacer esto, si espera que muestre posibles errores que una prueba unitaria no podría mostrar. Pero eso no es muy probable.

Probablemente esté mejor con las pruebas unitarias, al menos para empezar.

¿Debo poner una línea del archivo en una cadena constante?

Si va a escribir más de una prueba que utiliza la misma línea de entrada, entonces seguro. Pero personalmente, probablemente tendería a escribir un montón de pruebas diferentes, cada una pasando una cadena de entrada diferente. En ese punto, no hay muchas razones para hacer una constante (a menos que sea una constante local, declarada dentro del método de prueba).

¿Debo probar una o más líneas?

no se ha especificado, pero voy a asumir que su salida es de uno por uno con su entrada - es decir, si se pasa de tres cuerdas, obtendrá tres ITMData s devuelto. En ese caso, la necesidad de pruebas de líneas múltiples sería limitada.

Casi siempre vale la pena probar el caso degenerado, que en este caso sería un conjunto de cadenas vacío (cero líneas). Y probablemente valga la pena tener al menos una prueba que tenga más de una línea, para que pueda asegurarse de que no haya errores tontos en su iteración.

Sin embargo, si su salida es uno-por-uno con su entrada, entonces realmente tiene otro método que desea salir, debe tener un método ParseSingleLine. Entonces su Parse no sería más que iterar líneas y llamar al ParseSingleLine. Aún así, querría tener un puñado de pruebas para Parse, pero la mayoría de las pruebas se centrarían en ParseSingleLine.

+0

He editado mi pregunta. gracias hasta ahora. – Rookian

0

En cuanto a sus nuevas preguntas:

debo hacer la prueba a cada propiedad de ITMData o debería probar todo el objeto?

Si quiere estar seguro, probablemente debería tener al menos una prueba que verifique que cada propiedad se corresponde.

¿Qué pasa con el nombramiento de mi prueba?

Hay algunas discusiones sobre este tema, como this one. La regla general es que tendría múltiples métodos en su clase de prueba de unidad, cada uno destinado a probar algo específico. En su caso, podría ser cosas como:

public void Check_All_Properties_Parsed_Correctly(){.....} 

public void Exception_Thrown_If_Lines_Is_Null(){.....} 

public void Exception_Thrown_If_Lines_Is_Wrong_Length(){.....} 

tanto, en otras palabras, las pruebas para el comportamiento exacto que usted considera "correcta" para el analizador. Una vez hecho esto, se sentirá mucho más a gusto cuando realice cambios en el código del analizador, ya que tendrá un conjunto de pruebas completo para verificar que no haya roto nada. ¡Recuerde realizar pruebas frecuentemente y mantener sus pruebas actualizadas cuando realice cambios! Hay una guía bastante buena sobre pruebas unitarias y Desarrollo basado en pruebas en MSDN.

En general, creo que puede encontrar respuestas a la mayoría de sus preguntas buscando en Google un poco. También hay varios libros excelentes sobre Test Driven Development, que llevarán a casa no solo el cómo de TDD, sino el por qué. Si usted es relativamente independiente del lenguaje de programación, recomendaría Kent Beck's Test Driven Development By Example, de lo contrario algo como Test-Driven Development in Microsoft .NET. Esto debería llevarte por el buen camino muy rápido.

EDIT:

Am I overspecifying mis pruebas?

En mi opinión, sí. Específicamente, no estoy de acuerdo con su próxima línea:

Si pruebo todas las propiedades dentro de una clase, pierdo esta información.

¿De qué manera pierde información exactamente? Digamos que hay 2 maneras de hacer esta prueba, aparte de tener una nueva clase por prueba:

  1. tienen diferentes métodos para cada propiedad. Sus métodos de prueba se podrían llamar CheckPropertyX, CheckPropertyY, etc. Cuando ejecute sus pruebas, verá exactamente qué campos pasaron y cuáles campos fallaron. Esto claramente satisface tus requisitos, aunque diría que aún es excesivo. Me gustaría ir con la opción 2:
  2. Tiene algunos métodos diferentes, cada uno prueba un aspecto específico. Esto es lo que originalmente recomendé, y creo a lo que se refiere. Cuando una de las pruebas falla, solo obtendrá información sobre lo primero que falló, por método, pero si codificó bien su Assert, sabrá exactamente cuya propiedad es incorrecta. Considere el siguiente código:

Assert.AreEqual("test1", myObject.PropertyX, "Property X was incorrectly parsed"); Assert.AreEqual("test2", myObject.PropertyY, "Property Y was incorrectly parsed");

Cuando uno de los que falla, usted sabrá qué línea ha fallado. Cuando haya solucionado el error relevante y vuelva a ejecutar sus pruebas, verá si otras propiedades han fallado. En general, este es el enfoque que toma la mayoría de las personas, porque la creación de una clase o incluso un método por propiedad resulta en demasiado código y demasiado trabajo para mantenerse actualizado.

+0

He editado mi pregunta nuevamente. – Rookian

+0

OK, respondí a su edición, mi recomendación sería, a lo sumo, crear un nuevo método por propiedad para probar esa propiedad. –

+0

"¿De qué manera pierde información exactamente?" Machine.Specification permite generar informes HTML. Como puede ver, mis pruebas comienzan con "Cuando _ ...". Por lo tanto, el nombre de la clase es parte de la especificación. Si escribiera una clase para cada propiedad, tendría una especificación específica que contiene qué índice se usa para obtener una propiedad específica. – Rookian

Cuestiones relacionadas