c++ - para - generar codigo de barras en excel
¿Cómo debería probar la unidad un generador de código? (8)
Esta es una pregunta difícil y abierta que sé, pero pensé en tirarla al piso y ver si alguien tenía alguna sugerencia interesante.
He desarrollado un generador de código que lleva nuestra interfaz python a nuestro código C ++ (generado a través de SWIG) y genera el código necesario para exponer esto como WebServices. Cuando desarrollé este código, lo hice usando TDD, pero mis pruebas me parecieron frágiles como el infierno. Como cada prueba esencialmente quería verificar que para un bit dado de código de entrada (que resulta ser un encabezado C ++) obtuve un bit dado de código de salida. Escribí un pequeño motor que lee definiciones de prueba de archivos de entrada XML y genera pruebas casos de estas expectativas.
El problema es que me da miedo entrar para modificar el código. Eso y el hecho de que la unidad se prueba a sí misma son a: compleja, y b: frágil.
Así que estoy tratando de pensar acercamientos alternativos a este problema, y me parece que quizás lo estoy abordando de la manera incorrecta. Tal vez deba enfocarme más en el resultado, IE: ¿el código que genero realmente se ejecuta y hago lo que quiero, en lugar de que el código se vea como yo quiero?
¿Alguien ha tenido alguna experiencia de algo similar a esto que les gustaría compartir?
Empecé a escribir un resumen de mi experiencia con mi propio generador de código, luego volví y volví a leer tu pregunta y descubrí que ya habías abordado los mismos problemas, concéntrate en los resultados de la ejecución en lugar del diseño / aspecto del código.
El problema es que esto es difícil de probar, el código generado puede no ser adecuado para ejecutarse en el entorno del sistema de prueba de la unidad, y ¿cómo se codifican los resultados esperados?
Descubrí que necesita descomponer el generador de código en piezas más pequeñas y probarlas en unidades. La prueba unitaria de un generador de código completo se parece más a las pruebas de integración que a las pruebas unitarias si me preguntas.
Recuerde que las "pruebas unitarias" son solo un tipo de prueba. Debería poder probar las piezas internas de su generador de código. Lo que realmente está viendo aquí es la prueba de nivel del sistema (también conocida como prueba de regresión). No se trata solo de semántica ... hay diferentes mentalidades, enfoques, expectativas, etc. Ciertamente, es más trabajo, pero es probable que tenga que lidiar con el problema y establecer un conjunto de pruebas de regresión de extremo a extremo: archivos C ++ corregidos -> SWIG interfaces -> módulos de python -> salida conocida. Realmente desea comprobar la entrada conocida (código C ++ fijo) contra el resultado esperado (lo que sale del programa final de Python). Verificar los resultados del generador de código directamente sería como diferir los archivos de objetos ...
Sí, los resultados son lo único que importa. La verdadera tarea es escribir un marco que permita que el código generado se ejecute de forma independiente ... pase su tiempo allí.
Si está ejecutando * nux, podría considerar eliminar el marco de prueba de unitpro a favor de un script bash o makefile. en Windows podría considerar construir una aplicación / función de shell que ejecute el generador y luego use el código (como otro proceso) y pruebe eso.
Una tercera opción sería generar el código y luego crear una aplicación que no incluya nada más que una prueba unitaria. Nuevamente, necesitaría un script de shell o no lo haga para cada entrada. En cuanto a cómo codificar el comportamiento esperado, se me ocurre que podría hacerse de la misma manera que lo haría con el código C ++ simplemente utilizando la interfaz generada en lugar de la de C ++.
Solo quería señalar que todavía puede lograr pruebas detalladas mientras verifica los resultados: puede probar trozos individuales de código anidando dentro de algún código de configuración y verificación:
int x = 0;
GENERATED_CODE
assert(x == 100);
Siempre que tenga su código generado ensamblado a partir de fragmentos más pequeños, y los fragmentos no cambian con frecuencia, puede ejercitar más condiciones y probar un poco mejor, y con suerte evitar que se rompan todas las pruebas cuando cambie los detalles de un fragmento.
La prueba unitaria es solo que probar una unidad específica. Entonces, si está escribiendo una especificación para la clase A, es ideal si la clase A no tiene las versiones concretas reales de las clases B y C.
Ok, noté que la etiqueta para esta pregunta incluye C ++ / Python, pero los principios son los mismos:
public class A : InterfaceA
{
InterfaceB b;
InterfaceC c;
public A(InterfaceB b, InterfaceC c) {
this._b = b;
this._c = c; }
public string SomeOperation(string input)
{
return this._b.SomeOtherOperation(input)
+ this._c.EvenAnotherOperation(input);
}
}
Debido a que el Sistema A anterior inyecta interfaces a los sistemas B y C, usted puede probar solo el sistema A, sin tener que ejecutar ninguna funcionalidad real en ningún otro sistema. Esta es una prueba unitaria.
Aquí hay una manera inteligente de acercarse a un Sistema desde la creación hasta la finalización, con una especificación diferente Cuando para cada comportamiento:
public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
private string _actualString;
private string _expectedString;
private string _input;
private string _returnB;
private string _returnC;
[It]
public void Should_return_the_expected_string()
{
_actualString.Should().Be.EqualTo(this._expectedString);
}
public override void GivenThat()
{
var randomGenerator = new RandomGenerator();
this._input = randomGenerator.Generate<string>();
this._returnB = randomGenerator.Generate<string>();
this._returnC = randomGenerator.Generate<string>();
Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
.Return(this._returnB);
Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
.Return(this._returnC);
this._expectedString = this._returnB + this._returnC;
}
public override void WhenIRun()
{
this._actualString = Sut.SomeOperation(this._input);
}
}
Entonces, en conclusión, una sola unidad / especificación puede tener múltiples comportamientos, y la especificación crece a medida que desarrolla la unidad / sistema; y si su sistema bajo prueba depende de otros sistemas concretos dentro de él, tenga cuidado.
Mi recomendación sería descubrir un conjunto de resultados conocidos de entrada-salida, como algunos casos más simples que ya tiene instalados, y probar el código producido por la unidad . Es muy posible que al cambiar el generador, la secuencia exacta que se produce pueda ser ligeramente diferente ... pero lo que realmente importa es si se interpreta de la misma manera. Por lo tanto, si prueba los resultados ya que probaría ese código si fuera su característica, descubrirá si tiene éxito de la manera que desee.
Básicamente, lo que realmente quiere saber es si su generador producirá lo que espera sin probar físicamente todas las combinaciones posibles (también: imposible). Al asegurarse de que su generador sea consistente de la manera que espera, puede sentirse mejor de que el generador tenga éxito en situaciones cada vez más complejas.
De esta forma, también puede crear un conjunto de pruebas de regresión (pruebas unitarias que deben seguir funcionando correctamente). Esto lo ayudará a asegurarse de que los cambios en su generador no violen otras formas de código. Cuando encuentre un error que las pruebas de su unidad no captaron, puede incluirlo para evitar roturas similares.
Encuentro que necesitas probar lo que estás generando más que cómo lo generas.
En mi caso, el programa genera muchos tipos de código (C #, HTML, SCSS, JS, etc.) que se compilan en una aplicación web. La mejor manera que he encontrado para reducir los errores de regresión en general es probar la aplicación web en sí, no probando el generador.
No me malinterpreten, todavía hay pruebas unitarias que controlan parte del código del generador, pero nuestra mayor ganancia para nuestro dinero ha sido las pruebas de UI en la aplicación generada en sí.
Como lo estamos generando, también generamos una buena abstracción en JS que podemos utilizar para probar la aplicación de forma programática. Seguimos algunas ideas resumidas aquí: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089
La gran parte es que realmente prueba su sistema de extremo a extremo, desde la generación del código hasta lo que realmente está generando. Una vez que falla una prueba, es fácil seguirla hasta donde se rompió el generador.
Es muy dulce
¡Buena suerte!