visual unitarias unit tutorial test studio pruebas mvc clases biblioteca c# unit-testing code-generation

c# - tutorial - ¿Genera clases automáticamente desde pruebas unitarias?



pruebas unitarias c# visual studio 2017 (11)

Me parece que cada vez que necesito una herramienta de generación de código como esta, probablemente estoy escribiendo código que podría ser un poco más genérico, así que solo necesito escribirlo una vez. En su ejemplo, esos getters y setters no parecen estar agregando ningún valor al código; de hecho, es solo afirmar que el mecanismo getter / setter en C # funciona.

Me abstendría de escribir (o incluso usar) tal herramienta antes de entender cuáles son las motivaciones para escribir este tipo de pruebas.

Por cierto, es posible que desee echar un vistazo a NBehave ?

Estoy buscando una herramienta que pueda tomar una prueba de unidad, como

IPerson p = new Person(); p.Name = "Sklivvz"; Assert.AreEqual("Sklivvz", p.Name);

y generar, automáticamente, la clase y la interfaz del stub correspondiente

interface IPerson // inferred from IPerson p = new Person(); { string Name { get; // inferred from Assert.AreEqual("Sklivvz", p.Name); set; // inferred from p.Name = "Sklivvz"; } } class Person: IPerson // inferred from IPerson p = new Person(); { private string name; // inferred from p.Name = "Sklivvz"; public string Name // inferred from p.Name = "Sklivvz"; { get { return name; // inferred from Assert.AreEqual("Sklivvz", p.Name); } set { name = value; // inferred from p.Name = "Sklivvz"; } } public Person() // inferred from IPerson p = new Person(); { } }

Sé que ReSharper y Visual Studio hacen algunos de estos, pero necesito una herramienta completa, línea de comandos o lo que sea, que automáticamente infiere lo que se debe hacer. Si no existe tal herramienta, ¿cómo la escribiría (por ejemplo, extendiendo ReSharper, desde cero, utilizando qué bibliotecas)?


Si planea escribir su propia implementación, definitivamente le sugiero que eche un vistazo a los motores de plantillas NVelocity (C #) o Velocity (Java).

Los he usado anteriormente en un generador de códigos y he descubierto que hacen el trabajo mucho más fácil.



Me gusta CodeRush de DevExpress. Tienen un gran motor personalizable de plantillas. Y lo mejor para mí es que no hay cuadros de diálogo. También tienen funcionalidad para crear métodos e interfaces y clases desde la interfaz que no existe.


Es sorprendente cómo nadie realmente dio nada de lo que estabas preguntando.

No sé la respuesta, pero daré mis opiniones al respecto.

Si tuviera que intentar escribir algo como esto yo mismo, probablemente vería un plugin de resharper. La razón por la que digo eso es porque, como dijiste, el reafilador puede hacerlo, pero en pasos individuales. Así que escribía algo que iba línea por línea y aplicaba los métodos apropiados de creación de resharper encadenados.

Ahora, de ninguna manera, sé cómo hacer esto, ya que nunca he construido algo para resharper, pero eso es lo que trataría de hacer. Tiene sentido lógico que se pueda hacer.

Y si escribe algún código, POR FAVOR publíquelo, ya que podría encontrarlo también útil, pudiendo generar todo el esqueleto en un solo paso. Muy útil.


Es factible, al menos en teoría. Lo que haría sería usar algo como csparser para analizar la prueba unitaria (no puedes compilarla, desafortunadamente) y luego tomarla desde allí. El único problema que puedo ver es que lo que estás haciendo está mal en términos de metodología: tiene más sentido generar pruebas unitarias a partir de clases de entidad (de hecho, Visual Studio hace precisamente esto) que hacerlo al revés.


Intente mirar Pex, un proyecto de Microsoft sobre pruebas unitarias, que todavía está bajo investigación

research.microsoft.com/en-us/projects/Pex/


Lo que parece necesitar es un analizador para su lenguaje (Java) y un nombre y un tipo de resolución. ("Generador de tablas de símbolos").

Después de analizar el texto de origen, un compilador generalmente tiene una resolución de nombres, que intenta registrar la definición de nombres y sus tipos correspondientes, y un verificador de tipos, que verifica que cada expresión tenga un tipo válido.

Normalmente el nombre / tipo resolver se queja cuando no puede encontrar una definición. Lo que quiere hacer es encontrar el elemento "indefinido" que está causando el problema e inferir un tipo para él.

por

IPerson p = new Person();

el solucionador de nombres sabe que "Persona" e "IPerson" no están definidos. Si fuera

Foo p = new Bar();

no habría ninguna pista de que querías una interfaz, solo que Foo es un tipo de padre abstracto de Bar (por ejemplo, una clase o una interfaz). Por lo tanto, la herramienta debe saber qué decisión es la suya ("siempre que encuentre una construcción así, suponga que Foo es una interfaz ..."). Podría usar una heurística: IFoo y Foo significan que IFoo debería ser una interfaz, y en algún lugar alguien tiene que definir a Foo como una clase que se da cuenta de esa interfaz. Una vez que la herramienta haya tomado esta decisión, necesitaría actualizar sus tablas de símbolos para que pueda pasar a otras declaraciones:

por

p.Name = "Sklivvz";

dado que p debe ser una interfaz (según la inferencia anterior), Name debe ser un miembro del campo y su tipo es String de la asignación.

Con eso, la declaración:

Assert.AreEqual("Sklivvz", p.Name);

los nombres y tipos se resuelven sin mayor problema.

El contenido de las entidades IFoo y Foo depende de usted; no tenías que usar get y set pero ese es tu gusto personal.

Esto no funcionará tan bien cuando tenga múltiples entidades en la misma declaración:

x = p.a + p.b ;

Sabemos que a y b son campos probables, pero no se puede adivinar qué tipo numérico es si son numéricos, o si son cadenas (esto es legal para cadenas en Java, no sé sobre C #). Para C ++ ni siquiera sabes lo que significa "+"; podría ser un operador en la clase Bar. Entonces, lo que tiene que hacer es recopilar restricciones , por ejemplo, "a es un número o cadena indefinida", etc., y a medida que la herramienta recolecta evidencia, reduce el conjunto de posibles restricciones. (Esto funciona como esos problemas planteados: "Joe tiene siete hijos. Jeff es más alto que Sam. Harry no puede esconderse detrás de Sam. ... ¿quién es el gemelo de Jeff?" Donde tiene que reunir la evidencia y eliminar las imposibilidades). También debe preocuparse por el caso en el que termina con una contradicción.

Puede descartar el caso p.a + pb, pero luego no puede escribir las pruebas de su unidad con impunidad. Hay solucionadores de restricciones estándar por ahí si quieres impunidad. (Que concepto)

OK, tenemos las ideas, ahora, ¿se puede hacer esto de una manera práctica?

La primera parte de esto requiere un analizador y un nombre flexible y un resolvedor de tipos. Necesita un solucionador de restricciones o al menos una operación de "valores de valor definidos para el valor indefinido" (solucionador de restricciones triviales).

Nuestro DMS Software Reengineering Toolkit con su Java Front End probablemente podría hacer esto. DMS es una herramienta de creación de herramientas para personas que desean crear herramientas que procesen idiomas de forma arbitraria. (Piense en "calcular con fragmentos de programa en lugar de números").

DMS proporciona maquinaria de análisis de propósito general, y puede construir un árbol para cualquier front-end que se le dé (por ejemplo, Java, y hay un extremo frontal de C #). La razón por la que elegí Java es que nuestra interfaz Java tiene todo ese nombre y la maquinaria de resolución de tipo, y se proporciona en forma de fuente para que pueda ser doblada. Si te mantienes en el solucionador de restricciones triviales, probablemente puedas doblar la resolución del nombre de Java para descubrir los tipos. DMS le permitirá ensamblar árboles que corresponden a fragmentos de código, y combinarlos en otros más grandes; a medida que su herramienta recopile datos para la tabla de símbolos, podría construir los árboles primitivos.

En algún lugar, debes decidir que has terminado. ¿Cuántas pruebas unitarias debe ver la herramienta antes de conocer toda la interfaz? (¿Supongo que se come todas las que le das?). Una vez completado, ensambla los fragmentos para los distintos miembros y crea un AST para una interfaz; DMS puede usar su impresora bonita para convertir ese AST nuevamente en código fuente como lo ha demostrado.

Sugiero Java aquí porque nuestra interfaz Java tiene nombre y tipo de resolución. Nuestra parte frontal C # no. Esta es una "mera" cuestión de ambición; alguien tiene que escribir uno, pero eso es bastante trabajo (al menos fue para Java y no puedo imaginar que C # sea realmente diferente).

Pero la idea funciona bien en principio usando DMS.

Podrías hacer esto con alguna otra infraestructura que te diera acceso a un analizador sintáctico y un nombre flexible y resolución de tipo. Eso podría no ser tan fácil de obtener para C #; Sospecho que MS puede darte un analizador y acceso a nombre y tipo de resolución, pero no hay forma de cambiar eso. Quizás Mono es la respuesta?

Aún necesita un was para generar fragmentos de código y ensamblarlos. Puede intentar hacer esto pirateando cuerdas; mi (larga) experiencia con el pegado de bits del programa es que si lo haces con cadenas, eventualmente lo arruinas. Realmente quieres piezas que representen fragmentos de código de tipo conocido, que solo se puedan combinar de forma que permita la gramática; DMS lo hace así sin ensuciar.


Creo que lo que estás buscando es un kit de herramientas de fuzzing (https://en.wikipedia.org/wiki/Fuzz_testing).

Al ser duro, nunca lo usé, podrías darle a Randoop.NET la oportunidad de generar ''pruebas unitarias'' http://randoop.codeplex.com/


Creo que una verdadera solución a este problema sería un analizador muy especializado. Como eso no es tan fácil de hacer, tengo una idea más barata. Desafortunadamente, tendrías que cambiar la forma en que escribes tus pruebas (es decir, solo la creación del objeto):

dynamic p = someFactory.Create("MyNamespace.Person"); p.Name = "Sklivvz"; Assert.AreEqual("Sklivvz", p.Name);

Se usaría un objeto de fábrica. Si puede encontrar el objeto nombrado, lo creará y lo devolverá (esta es la ejecución de prueba normal). Si no lo encuentra, creará un proxy de grabación (un DynamicObject ) que registrará todas las llamadas y al final (tal vez al derribarlo) podría emitir archivos de clase (tal vez basados ​​en algunas plantillas) que reflejen lo que "vio " siendo llamado.

Algunas desventajas que veo:

  • Necesita ejecutar el código en "dos" modos, lo cual es molesto.
  • Para que el proxy pueda "ver" y grabar llamadas, deben ejecutarse; de modo que el código en un bloque catch , por ejemplo, tiene que ejecutarse.
  • Tienes que cambiar la forma en que creas tu objeto bajo prueba.
  • Tienes que usar dynamic ; perderá seguridad en tiempo de compilación en las siguientes ejecuciones y tendrá un impacto en el rendimiento.

La única ventaja que veo es que es mucho más barato de crear que un analizador especializado.


Visual Studio incluye algunas características que pueden ser útiles para usted aquí:

Generar el trozo de método Cuando escribe una llamada a un método que no existe, obtendrá una pequeña etiqueta inteligente en el nombre del método, que puede usar para generar un apéndice del método en función de los parámetros que está pasando.

Si eres una persona de teclado (yo soy), justo después de escribir el paréntesis de cierre, puedes hacer:

  • Ctrl-. (para abrir la etiqueta inteligente)
  • ENTER (para generar el stub)
  • F12 (ir a la definición, para llevarlo al nuevo método)

La etiqueta inteligente solo aparece si el IDE piensa que no hay un método que coincida. Si desea generar cuando la etiqueta inteligente no está activa, puede ir a Editar-> Intellisense-> Generar trozo de método .

Fragmentos . Plantillas de código pequeño que facilitan la generación de bits de código común. Algunos son simples (prueba "si [TAB] [TAB]"). Algunos son complejos (''switch'' generará casos para una enumeración). También puedes escribir el tuyo. Para su caso, pruebe "clase" y "prop".

Consulte también " Cómo cambiar" Generar Stub de método "para arrojar NotImplementedException en VS? " Para fragmentos de información en el contexto de GMS.

autoprops . Recuerde que las propiedades pueden ser mucho más simples:

public string Name { get; set; }

crear clase En el Explorador de soluciones, haga clic en el nombre del proyecto o una subcarpeta, seleccione Agregar-> Clase . Escriba el nombre de su nueva clase. Presione ENTER . Obtendrás una declaración de clase en el espacio de nombres correcto, etc.

Implementar la interfaz . Cuando desee que una clase implemente una interfaz, escriba la parte del nombre de la interfaz, active la etiqueta inteligente y seleccione cualquiera de las opciones para generar stubs para los miembros de la interfaz.

Estas no son exactamente la solución 100% automatizada que está buscando, pero creo que es una buena mitigación.