¿Alguien está haciendo TDD "real" con Visual-C ++, y si es así, ¿cómo lo hacen? [cerrado]


10

Test Driven Development implica escribir la prueba antes del código y seguir un ciclo determinado :

  • Prueba de escritura
  • Comprobar prueba (ejecutar)
  • Escribir código de producción
  • Comprobar prueba (ejecutar)
  • Limpiar código de producción
  • Comprobar prueba (ejecutar)

En lo que a mí respecta, esto solo es posible si su solución de desarrollo le permite cambiar muy rápidamente entre la producción y el código de prueba, y ejecutar la prueba para una determinada parte del código de producción extremadamente rápido.

Ahora, si bien existen una gran cantidad de infraestructuras de prueba Unidad para C ++ (estoy usando Bost.Test atm.) Sí parece que hay no significa realmente existe alguna decente (para C ++ nativo solución) Visual Studio (Plugin) que hace que el TDD ciclo soportable independientemente del marco utilizado.

"Bearable" significa que es una acción de un clic para ejecutar una prueba para un determinado archivo cpp sin tener que configurar manualmente un proyecto de prueba por separado, etc. "Bearable" también significa que se inicia una prueba simple (vinculación) y se ejecuta muy rápidamente .

Entonces, ¿qué herramientas (complementos) y técnicas existen que hacen posible el ciclo TDD para el desarrollo nativo de C ++ con Visual Studio?

Nota: estoy bien con herramientas gratuitas o "comerciales".

Por favor : No hay recomendaciones marco. (A menos que el marco tenga un complemento de Visual Studio dedicado y desee recomendar el complemento).


Nota de edición : Las respuestas hasta ahora han proporcionado enlaces sobre cómo integrar un marco de Prueba de unidad en Visual Studio. Los recursos más o menos describen cómo hacer que el marco UT compile y ejecute sus primeras pruebas. De esto no se trata esta pregunta. Soy de la opinión de que para trabajar realmente de manera productiva, teniendo las Pruebas de Unidad en un vcproj mantenido manualmente (!), Vcproj separado de sus clases de producción agregará tanta sobrecarga que TDD "no es posible". Hasta donde yo sé, no agrega "proyectos" adicionales a una cosa Java o C # para habilitar las pruebas unitarias y TDD, y por una buena razón. Esto debería es posible con C ++ con las herramientas adecuadas, pero parece (esta pregunta es) que hay muy pocas herramientas para TDD / C ++ / VS.


Buscando en Google, he encontrado una herramienta, VisualAssert , que parece apuntar en la dirección correcta. Sin embargo, afaiks, no parece tener un uso generalizado (en comparación con CppUnit, Boost.Test, etc.).


Editar: me gustaría agregar un comentario al contexto de esta pregunta. Creo que hace un buen resumen de delinear (parte del problema): (comentario de Billy ONeal )

Visual Studio no utiliza "scripts de compilación" que el usuario pueda editar razonablemente. Un proyecto produce un binario. Además, Java tiene la propiedad de que Java nunca construye un binario completo: el binario que construye es solo un ZIP de los archivos de clase. Por lo tanto, es posible compilar por separado y luego JAR juntos manualmente (por ejemplo, usando 7z). C ++ y C # ambos vinculan sus binarios, por lo que, en general, no se puede escribir un script como ese. Lo más cercano que puede obtener es compilar todo por separado y luego hacer dos enlaces (uno para producción y otro para prueba).


2
As far as I am aware, you do not add extra "projects" to a Java or C# thing to enable Unit Tests and TDD,<- No creo que esto sea correcto. Por lo general, también tiene varios proyectos en C #; no desea enviar su código de prueba en su binario de producción.
Billy ONeal

2
Nunca he visto eso manejado por el marco. La generación de un binario requiere un proyecto. Quieres dos binarios; uno con código de prueba y otro con código de producción. Por lo tanto, necesita dos proyectos. No hay forma de evitar eso.
Billy ONeal

1
@BillyONeal, en todos menos uno de mis proyectos (Java), el proyecto contiene la fuente principal y de prueba: el script de compilación luego selecciona qué piezas poner en los artefactos desplegables.
Robert Mark Bram

2
@Robert: Visual Studio no utiliza "scripts de compilación" que el usuario pueda editar razonablemente. Un proyecto produce un binario. Además, Java tiene la propiedad de que Java nunca construye un binario completo: el binario que construye es solo un ZIP de los archivos de clase. Por lo tanto, es posible compilar por separado y luego JAR juntos manualmente (usando, por ejemplo 7z) C ++ y C # ambos vinculan sus binarios, por lo que, en general, no se puede escribir un script como ese. Lo más cercano que puede obtener es compilar todo por separado y luego hacer dos enlaces (uno para producción y otro para prueba).
Billy ONeal

44
¿Seriamente? "Consejo: cada prueba debe contener una función principal y generar un ejecutable". Esto suena ridículamente lento para cualquier cantidad razonable de pruebas. Incluso si significan solo un accesorio de prueba por ejecutable, sigue siendo un consejo tonto de la OMI. Intente hacerlo con miles de pruebas (para un proyecto de tamaño mediano) o cientos de miles de pruebas (para un proyecto grande) y definitivamente se volverá loco.
legalizar el

Respuestas:


4

He escrito una serie de blogs de 5 partes sobre cómo hacer TDD con C ++ y Visual Studio: parte 1 , parte 2 , parte 3 , parte 4 , parte 5 .

No estoy seguro de por qué dice que no hace proyectos adicionales en C # para hacer TDD, porque eso es lo que siempre he hecho con NUnit y parece típico de lo que otras personas también hacen con NUnit. La razón es simple: siempre mantenga el código de prueba separado del código de producción. Para C ++ con Visual Studio, esto significa proyectos separados al igual que para C # y NUnit. Por lo que sé del mundo de Java, esto también es común allí.

Obviamente, todos tienen ideas diferentes de lo que es "soportable" para hacer TDD. He estado practicando el método que describo en mi publicación de blog durante años severos y lo encuentro muy soportable. C ++ es un lenguaje compilado y las cosas de compilación pueden ser lentas cuando el sistema bajo prueba está altamente acoplado. Simplemente no hay forma de alejarse de eso sin refactorizar a un diseño más débilmente acoplado.

Mi "acción de un clic" es "Build Solution". Si se está construyendo demasiado, siempre puede descargar proyectos irrelevantes mientras trabaja y luego Build Solution creará solo el subconjunto mínimo de proyectos necesarios para actualizarse como resultado de sus cambios.

De acuerdo, la naturaleza del tiempo de compilación de C ++ hace que esto tome un poco más de tiempo en cada ciclo del proceso TDD que lo que hizo con NUnit y C #, pero la confianza que obtengo de mi código C ++ bien probado vale la pena. De lo contrario, pasaré mucho más tiempo en el depurador. Le advertiría que se ahorre en el uso de gmock, ya que puede aumentar sustancialmente el tiempo de compilación de prueba. Hasta ahora me he salido con la mayoría de los objetos "falsos" livianos y rara vez necesito la funcionalidad completa de los simulacros. Los marcos de simulación para C ++ se basan en gran medida en plantillas y esto puede aumentar significativamente el tiempo de compilación, por lo que deben reservarse para donde realmente necesita un simulacro y un falso simplemente no funcionará.

He considerado crear un asistente de proyecto de prueba de unidad Boost.Test para automatizar parte de la naturaleza de la placa de la caldera de crear el proyecto de prueba para el código de producción, pero después de haberlo hecho un par de veces, realmente no es tan difícil hacerlo manualmente.

En cuanto a las soluciones con muchos proyectos (¿150?), También hay formas de hacer frente a eso. Una obvia es encontrar grupos de proyectos relacionados y agruparlos y comenzar a consumirlos / publicarlos como una unidad. Si realmente necesita reconstruir / tocar los 150 proyectos para los pequeños cambios que está realizando durante un ciclo TDD, entonces su código está tan acoplado de todos modos que no es probable que las pruebas unitarias hagan una gran diferencia.

Al mirar el enlace IDE de netbeans, encuentro el atractivo de tener algo que analiza el resultado de la prueba y muestra una pequeña fila de prueba en una ventana con un símbolo verde o rojo al lado, algo que pensé que extrañaría haber venido de NUnit, pero en realidad no faltó. Me pareció más útil que la compilación simplemente fallara y luego podría hacer doble clic en la ventana de errores para colocar el cursor en la ubicación de la afirmación fallida.


"... no estoy seguro de por qué dices que no haces proyectos adicionales en C # ... Por lo que sé del mundo de Java, esto también es común allí ..." Puede haber sido un error de mi parte. (Para .NET al menos, porque necesita un ejecutable para .NET; para Java solo necesita los archivos de clase, por lo que no veo exactamente dónde encajaría el proyecto adicional).
Martin Ba,

No estoy seguro de cómo funciona Java con proyectos; Tengo una experiencia limitada allí. Por lo poco que sé, entiendo que los proyectos son un artefacto del IDE y no el lenguaje. (Hablando estrictamente, esto también es cierto para C #, pero no conozco a nadie que solo use el compilador de línea de comandos para cualquier cosa que no sea artículos de blog cortos o demostraciones). Sin embargo, incluso en Java definitivamente mantiene el código de prueba separado de el código de producción, que es lo que los proyectos separados están haciendo por ti. Nunca recomiendo compilar condicionalmente C ++ para separar la producción y el código de prueba.
legalizar el

1
"mantenga el código de prueba separado del código de producción, que es lo que los proyectos separados están haciendo por usted" - ¡Ajá! Bueno, en realidad no, en mi humilde opinión. El "proyecto" separado es una necesidad para C ++ y .NET, ya que ambos necesitan crear un ejecutable para ejecutar cualquier cosa y para crear un (uno) ejecutable necesita un (un) proyecto. Estoy bien con mantener el código de prueba separado (o no) del código de producción, pero encuentro molesto tener que agregar un "proyecto" (redundante) para generar el ejecutable de prueba. :-)
Martin Ba

1
Necesita dos productos integrados: código de producción (biblioteca estática, biblioteca compartida, ejecutable, etc.) y un ejecutable de prueba. En Visual Studio, cada producto creado corresponde a un proyecto, por lo que necesita dos proyectos. Realmente no es más complicado que eso. El proyecto de prueba unitaria NO es redundante.
legalizar

2

No estoy usando Visual-C ++, pero estoy realizando TDD con C ++, usando googletest y googlemock, con QtCreator como mi IDE. Hace años tuve una configuración similar con Visual-C ++ pero usando un marco de prueba de unidad diferente.

Lo que he encontrado útil es separar el proyecto en algunos subproyectos.

  1. Una biblioteca estática o dinámica que contiene el 99% del código fuente real del proyecto.
  2. Un proyecto que consiste principalmente en un método main () para ejecutar el programa normal.
  3. Un proyecto de prueba que contiene un main () para ejecutar mi marco de prueba y muchos archivos que contienen pruebas y objetos simulados.

Con esta configuración, mi IDE se encarga de agregar archivos a varios proyectos y, si las dependencias se determinan correctamente, puedo ejecutar todas las pruebas de mi unidad con una reconstrucción parcial. Incluso lo tengo configurado actualmente para ejecutar todas mis pruebas inmediatamente después de construir. Jenkins, el CI que estoy usando actualmente, también ejecuta y proporciona resultados de pruebas y datos de cobertura.

Es posible agregar un iniciador personalizado en su IDE para que un archivo ejecute las pruebas unitarias para el archivo Foo.cpp si nombra todas las pruebas unitarias para Foo en el dispositivo de prueba TestFoo. Cómo configurar esto precisamente para Visual-C ++ No estoy seguro, pero creo que es posible.


Información útil, aunque iría tan lejos como para llamarlo el "consejo común" :-) ... tampoco es realmente factible para nuestras cosas heredadas, pero agregar pruebas a las cosas heredadas es un dolor de todos modos ( y me hubiera gustado por lo menos facilitar la adición del aparejo de prueba).
Martin Ba

2

Yo uso MSTest para probar el código nativo de C ++.
Aquí está la gran publicación de blog sobre esta manera: http://blogs.msdn.com/b/jsocha/archive/2010/11/19/writing-unit-tests-in-visual-studio-for-native-c. aspx

Sí, habrá al menos dos proyectos: uno para la aplicación en sí, otro para las pruebas.
En lugar de hacer un tercer proyecto con una biblioteca estática, solo agrego el origen de la aplicación para probar el proyecto, de modo que la solución se ve así:

[-] Solution 'Foo'      Foo\Foo.sln
 |-[-] Foo              Foo\Foo\Foo.vcproj
 |  |-[-] include
 |  |  |- foo.h         Foo\Foo\foo.h
 |  |  |- bar.h         Foo\Foo\bar.h
 |  |
 |  |-[-] source
 |     |- foo.cpp       Foo\Foo\foo.cpp
 |
 |-[-] Foo.Tests        Foo\Foo.Tests\Foo.Tests.vcproj
    |                        (Additional include directory: "..\Foo")
    |-[-] include
    |  |- FakeBar.h     Foo\Foo.Tests\FakeBar.h
    |
    |-[-] source
       |-[-] app
       |  |- foo.cpp    Foo\Foo\foo.cpp    -- not in Foo.Tests\
       |
       |-[-] unit-tests
          |- foo_Tests.cpp   Foo\Foo.Tests\foo_Tests.cpp
          |- bar_Tests.cpp   Foo\Foo.Tests\bar_Tests.cpp

Me parece que usar el material C ++ / CLI para las pruebas unitarias simplemente enturbia las aguas al probar el código nativo puro de C ++. Sin embargo, he usado NUnit para probar el código de la aplicación C ++ / CLI. Escribí mis pruebas en C # y eso funcionó bien. (El código base existente era C ++ / CLI y no quería portarlo a C #.)
legalice el

Además, si sus pruebas están en C ++ / CLI, entonces no puede ejecutarlas en otras plataformas. La mayoría de los lugares donde he usado C ++ necesitaban la capacidad de compilación multiplataforma. Por supuesto, no puede reutilizar el proyecto VS en otras plataformas, pero no es un gran problema tener Makefiles o SConscripts para él.
legalizar el

@legalize No podemos reutilizar WinAPI (y COM y otras tecnologías específicas de Windows) en plataformas que no sean de Windows también.
Abyx

Sí, por supuesto, no puede usar tecnologías específicas de Windows en plataformas que no sean Windows. Mi observación fue simplemente que si tiene un código agnóstico de plataforma, entonces no desea vincular sus pruebas unitarias a una plataforma en particular. En un empleador anterior, evaluamos una gran cantidad de marcos y métodos de prueba unitaria. Elegimos Boost.Test porque era multiplataforma y si algo terminara en la biblioteca estándar de C ++ con respecto a las pruebas unitarias, probablemente sería Boost.Test.
legalizar el

2

Tal vez un poco tarde en el día, pero si leo su pregunta correctamente, ¿está buscando técnicas para mejorar el ciclo TDD? No se ha mencionado aquí, pero ¿ha mirado los eventos posteriores a la compilación en VS?

Nuestras soluciones suelen estar organizadas (se muestran las dependencias del proyecto) ...

MAIN-APP > LIB1, LIB2, UNIT-TEST-APP
UNIT-TEST-LIB1 > LIB1
UNIT-TEST-LIB2 > LIB2
UNIT-TEST-APP > UNIT-TEST-LIB1, UNIT-TEST-LIB2

El evento posterior a la compilación de MAIN-APP ejecutará UNIT-TEST-APP

El evento posterior a la compilación de UNIT-TEST-APP se ejecutará solo (solo ponga '$ (TargetPath)' como el comando para ejecutar en el evento posterior a la compilación).

(Esto significa que al construir la APLICACIÓN PRINCIPAL, las pruebas unitarias pueden ejecutarse dos veces, ¡pero eso no ha sido realmente un problema en nuestro caso!)

Sin embargo, como se mencionó, sí, hay un poco de esfuerzo en configurar esta estructura, pero una vez que está allí, agregar pruebas es simple.

¡Entonces todo lo que tiene que hacer es compilar la aplicación principal y las pruebas unitarias se ejecutarán automáticamente!


1

Bueno, no sé si esto ayuda, pero hay algunos videos excelentes sobre TDD de Brett L. Schuchert. Desafortunadamente, no muestra la combinación "C ++" y "VS", pero

TDD con C # y VS: http://vimeo.com/album/210446

TDD con C ++ y Eclipse: http://vimeo.com/13240481

Quizás puedas resolverlo con esos dos.

EDITAR: el video C ++ trata sobre el uso del marco de prueba CppUTest con Eclipse, por supuesto. Cuando lo publiqué, pensé que debería adoptarse fácilmente para usar en VS. Así que busqué en Google un poco y encontré esto:

http://schuchert.wikispaces.com/tdd.cpp.NotesOnCppUTest

que le brinda información sobre cómo usar CppUTest en Visual Studio.


2
Doc: solo he visto el video TDD / Eclipse, pero voy a rechazar este. El video muestra exactamente lo que no me interesa, a saber, cómo escribir el código de Prueba de Unidad. El problema (de esta pregunta) no es escribir pruebas unitarias, es cómo integrarlas adecuadamente con su desarrollo de producción C ++ y no veo cómo estos videos ayudan aquí.
Martin Ba

Si bien rechacé esta respuesta en el contexto de esta pregunta, me gustaría agregar que los videos son bastante agradables. Encontré el Eclipse / TDD / C ++ interesante para ver. Simplemente no ayuda aquí :-)
Martin Ba

@ Martin: mira mi edición.
Doc Brown

Apreciamos su esfuerzo y, aunque no creo que este enlace adicional sea realmente útil en el contexto de esta pregunta, creo que tendré que editarlo yo mismo.
Martin Ba

@ Martin: ok, leí tu pregunta nuevamente y tu definición de "soportable", pero ¿no esperas demasiado? Configurar un proyecto de prueba unitaria no es una solución de "un clic", pero el esfuerzo de escribir las pruebas unitarias reales supera eso en órdenes de magnitud, sea cual sea el marco que esté utilizando.
Doc Brown

1

Googletest
Cómo integrarse con vc ++

No necesita un complemento, la prueba es solo otro objetivo. No hay complementos para generar pruebas con c ++, evne si pudieras estar probando cosas sin sentido como asignaciones


"incluso si pudieras, estaría probando cosas sin sentido como tareas", ¿qué se supone que significa eso? ¿Realmente crees que un mejor soporte IDE para pruebas unitarias en C ++ no tiene valor?
Martin Ba

Quiero decir, en un lenguaje como c ++, el sistema no puede crear automáticamente pruebas para otra cosa que no sean declaraciones obvias
Martin Beckett,

2
Por qué no? ¿Qué impide que un complemento genere automáticamente un vcprojarchivo sobre la marcha, extrayendo el archivo de prueba que he escrito y el archivo de producción al que se hace referencia e intentando ejecutarlo? (Solo soñando, pero podría hacerse funcionar.)
Martin Ba

2
No estoy seguro de poder seguirlo. obviamente tengo que escribir las pruebas yo mismo. Pero ejecutarlos podría ser mucho más fácil que tener que configurar manualmente (¡y mantener!) Archivos de proyecto de prueba separados.
Martin Ba

2
No, no me referí al código de prueba, sino más bien al andamiaje del proyecto / compilador necesario para obtener el código de prueba más "es" código de producción para ejecutar.
Martin Ba

1

No puedo comentar sobre las herramientas de C ++, ya que no he tocado en unos 20 años (desarrollo de .NET en estos días) y supongo que la mayoría de las herramientas en estos días son para código administrado, pero en cuanto a técnicas ...

Como otros han mencionado, el código de prueba siempre se encuentra en un proyecto / ensamblaje completamente diferente al código de producción y sí, por lo general, debe mantener ese proyecto usted mismo, aunque ciertamente en VS IDE esto no es un gran problema, ya que a menudo tiene varios proyectos como parte de su solución de todos modos.

El código de producción es y debe escribirse un poco diferente para TDD. A medida que escribe las pruebas primero, termina teniendo que diseñar su código para que sea comprobable. Este es otro tema en sí mismo, pero puede tomar tiempo y parecer muy frustrante al principio, especialmente si su IDE / herramientas no le brindan comentarios rápidos, el lanzamiento de herramientas de línea de comandos para ejecutar pruebas es simplemente perjudicial.

Hay muchas técnicas específicas para hacer que el código sea comprobable, pero la mayoría de ellas se descomponen en la creación de objetos pequeños que no hacen mucho para que pueda probarlos de forma aislada y poder inyectar una versión de prueba de algún comportamiento en un entorno más complejo. objetos. Los marcos del COI pueden ayudar mucho aquí.

Un libro que puede encontrar útil es; Michael Feathers, trabajando eficazmente con código heredado. Utiliza varios idiomas en sus ejemplos y puede ayudarlo a identificar enfoques específicos para adaptar de forma segura códigos / técnicas que no fueron diseñados originalmente para ser comprobables.

Pequeña advertencia: bebí del Agile Kool-Aid hace años: D


Nota: Ya tengo Working Effectively with Legacy Codeen mi escritorio :-)
Martin Ba

0

Maven no se usa ampliamente en C ++ (sin embargo, se usa principalmente para Java pero es independiente del lenguaje), pero es una herramienta muy poderosa y le permite mantener todo en un solo proyecto (incluidas las pruebas, de hecho, eso es lo recomendado acercamiento con Maven). Solo lo sugiero ahora, ya que a partir de las respuestas hasta ahora parece que una alternativa con un complemento VS puede no existir.

Buscando complementos encontré:

http://incubator.apache.org/npanday/

pero aún no parece ser muy maduro. Con la configuración de Maven, todo lo que necesita hacer para ejecutar las pruebas se ejecuta mvn testen la línea de comandos.

Si está interesado, puede obtener información aquí y (uno de) los complementos compatibles con C ++ aquí (Maven tiene una arquitectura de complementos, por lo que todo es un complemento).


0

Recomendación de marco: en nuestra oficina, utilizamos TestDriven.NET que se integra con Visual Studio. Las clases de unittest se escriben en C ++ / CLI, que luego puede llamar para ejercer cualquier código nativo que necesite probar. Sí, las clases C ++ / CLI entran en su propio ensamblado, por lo tanto, se agrega un proyecto de "prueba" a las soluciones.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.