Escribir pruebas unitarias para C/C++ con el Framework de pruebas unitarias de Microsoft para C++
En Visual Studio puede crear pruebas unitarias para código no administrado escrito en C++.El código no administrado se denomina a veces código nativo.
El siguiente procedimiento contiene la información esencial para empezar.Las secciones posteriores proporcionan un tutorial que describe los pasos más detalladamente.
Escribir pruebas unitarias para un archivo DLL de código no administrado
Utilice la plantilla Proyecto de prueba nativo para crear un proyecto de Visual Studio independiente para las pruebas.
El proyecto contiene ejemplos de código de prueba.
Haga que el archivo DLL esté accesible para el proyecto de prueba:
Use #include para agregar un archivo .h que contiene declaraciones de las funciones del archivo DLL accesibles externamente.
El archivo .h debería contener declaraciones de función marcadas con _declspec(dllimport).Como alternativa, puede exportar los métodos mediante un archivo DEF.Para obtener más información, consulta Importar y exportar.
Las pruebas unitarias pueden acceder solo a las funciones que se exportan desde la DLL de prueba.
Agregue el proyecto DLL a las referencias del proyecto de prueba:
En las Propiedades del proyecto de prueba, expanda Propiedades comunes, Framework y Referencias y elija Agregar referencia.
En el proyecto de prueba, cree las clases y métodos de prueba mediante las macros de prueba y la clase Assert de la siguiente manera:
#include "stdafx.h" #include <CppUnitTest.h> #include "..\MyProjectUnderTest\MyCodeUnderTest.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; TEST_CLASS(TestClassName) { public: TEST_METHOD(TestMethodName) { // Run a function under test here. Assert::AreEqual(expectedValue, actualValue, L"message", LINE_INFO()); } }
Assert contiene diversas funciones estáticas que puede usar para comprobar el resultado de una prueba.
El parámetro LINE_INFO() es opcional.En los casos donde no hay ningún archivo PDB, permite al ejecutor de pruebas identificar la ubicación de un error.
También puede escribir métodos de instalación y limpieza de prueba.Para obtener más información, abra la definición de la macro TEST_METHOD y lea los comentarios sobre CppUnitTest.h
No se pueden anidar las clases de prueba.
Utilice el Explorador de pruebas para ejecutar las pruebas:
En el menú Ver, elija Otras ventanas, Explorador de pruebas.
Compile la solución de Visual Studio.
En el Explorador de pruebas, elija Ejecutar todas.
Para investigar cualquier prueba con más detalle en el Explorador de pruebas:
Seleccione el nombre de la prueba para ver más detalles, como mensajes de error y el seguimiento de pila.
Abra el nombre de la prueba (por ejemplo, haciendo doble clic) para ir a la ubicación del error o al código de prueba.
En el menú contextual para una prueba, elija Depurar prueba seleccionada para ejecutar la prueba en el depurador.
Tutorial: Desarrollar una DLL no administrada con el Explorador de pruebas
Puede adaptar este tutorial para desarrollar su propio archivo DLL.Los principales pasos son los siguientes:
Crear un proyecto de prueba nativo.Las pruebas se crean en un proyecto independiente del archivo DLL que se está desarrollando.
Crear un proyecto DLL.Este tutorial crea un nuevo archivo DLL, pero el procedimiento para probar una DLL existente es similar.
Hacer visibles para las pruebas las funciones DLL.
Aumentar de forma iterativa las pruebas.Se recomienda un ciclo "rojo-verde-refactorización" en el que el desarrollo del código lo dirigen las pruebas.
Depurar errores de las pruebas.Puede ejecutar pruebas en modo de depuración.
Refactorizar mientras mantiene las pruebas sin cambios.Refactorizar significa mejorar la estructura del código sin cambiar su comportamiento externo.Puede hacerlo para mejorar el rendimiento, la extensibilidad o la legibilidad del código.Dado que la intención es no cambiar el comportamiento, no cambie las pruebas al realizar un cambio de refactorización en el código.Las pruebas le permiten asegurarse de que no crea nuevos errores mientras realiza la tarea de refactorizar.Por lo tanto, puede realizar estos cambios con mucha más confianza que si no tuviera las pruebas.
Comprobar cobertura.Las pruebas unitarias son más útiles cuanto más código ponen a prueba.Puede detectar qué partes del código han utilizado las pruebas.
Aislar las unidades de los recursos externos.Normalmente, un archivo DLL depende de otros componentes del sistema que está desarrollando, como otros archivos DLL, bases de datos o subsistemas remotos.Es útil probar cada unidad aislada de sus dependencias.Los componentes externos pueden ralentizar las pruebas.Durante el desarrollo, los demás componentes podrían no estar completos.
Crear un proyecto de prueba unitaria nativo
En el menú Archivo, elija Nuevo, Proyecto.
En el cuadro de diálogo, expanda Instalado, Plantillas, Visual C++ y Prueba.
Elija la plantilla Proyecto de prueba nativo.
En este tutorial, el proyecto de prueba se llama NativeRooterTest.
En el nuevo proyecto, inspeccione unittest1.cpp
Tenga en cuenta que:
Cada prueba se define mediante TEST_METHOD(YourTestName){...}.
No es necesario escribir una firma de función convencional.La firma se crea mediante la macro TEST_METHOD.La macro genera una función de la instancia que devuelve void.También genera una función estática que devuelve información sobre el método de prueba.Esta información permite al Explorador de pruebas encontrar el método.
Los métodos de prueba se agrupan en clases mediante el uso de TEST_CLASS(YourClassName){...}.
Cuando se ejecutan las pruebas, se crea una instancia de cada clase de prueba.Se llama a los métodos de prueba en un orden no especificado.Puede definir métodos especiales que se invocan antes y después de cada módulo, clase o método.Para obtener más información, vea Organizar pruebas en C++.
Compruebe que las pruebas se ejecutan en el Explorador de pruebas:
Inserte código de prueba:
TEST_METHOD(TestMethod1) { Assert::AreEqual(1,1); }
Tenga en cuenta que la clase Assert proporciona varios métodos estáticos que puede usar para comprobar los resultados de los métodos de prueba.
En el menú Prueba, elija Ejecutar, Todas las pruebas.
La prueba se compila y ejecuta.
Aparece el Explorador de pruebas.
La prueba aparece en Pruebas superadas.
Crear un proyecto de DLL no administrado
Cree un proyecto de Visual C++ usando la plantilla Proyecto Win32.
En este tutorial, el proyecto se llama RootFinder.
Seleccione DLL y Exportar símbolos en el asistente para aplicaciones Win32.
La opción Exportar símbolos genera una cómoda macro que puede utilizar para declarar métodos exportados.
Declare una función exportada en el archivo .h principal:
El declarador __declspec(dllexport) hace que los miembros públicos y protegidos de la clase sean visibles fuera del archivo DLL.Para obtener más información, consulta Usar dllimport y dllexport en las clases de C++.
En el archivo .cpp principal, agregue un cuerpo mínimo para la función:
// Find the square root of a number. double CRootFinder::SquareRoot(double v) { return 0.0; }
Acoplar el proyecto de prueba al proyecto DLL
Agregue el proyecto DLL a las referencias del proyecto de prueba:
Abra las propiedades del proyecto de prueba y elija Propiedades comunes, Framework y Referencias.
Elija Agregar nueva referencia.
En el cuadro de diálogo Agregar referencia, seleccione el proyecto DLL y elija Agregar.
En el archivo .cpp de prueba unitaria principal, incluya el archivo .h del código DLL:
#include "..\RootFinder\RootFinder.h"
Agregue una prueba básica que utiliza la función exportada:
TEST_METHOD(BasicTest) { CRootFinder rooter; Assert::AreEqual( // Expected value: 0.0, // Actual value: rooter.SquareRoot(0.0), // Tolerance: 0.01, // Message: L"Basic test failed", // Line number - used if there is no PDB file: LINE_INFO()); }
Compile la solución.
La nueva prueba aparece en el Explorador de pruebas.
En el Explorador de pruebas, elija Ejecutar todas.
Ha configurado la prueba y los proyectos de código, y ha verificado que puede ejecutar las pruebas que ejecutan funciones en el proyecto de código.Ahora puede empezar a escribir pruebas y código reales.
Aumentar las pruebas de forma iterativa y comprobar si se superan
Agregue una nueva prueba:
TEST_METHOD(RangeTest) { CRootFinder rooter; for (double v = 1e-6; v < 1e6; v = v * 3.2) { double actual = rooter.SquareRoot(v*v); Assert::AreEqual(v, actual, v/1000); } }
Sugerencia Se recomienda no cambiar las pruebas superadas.En vez de ello, agregue una nueva prueba, actualice el código para que la prueba se supere, después agregue otra prueba y así sucesivamente.
Cuando los usuarios cambien los requisitos, deshabilite las pruebas que ya no son correctas.Escriba nuevas pruebas y hágalas funcionar una a una de la misma manera incremental.
Compile la solución y, en el Explorador de pruebas, elija Ejecutar todo.
Se produce un error en la nueva prueba.
Sugerencia Compruebe que todas las pruebas producen un error inmediatamente después de escribirlas.Esto ayuda a evitar el error habitual de escribir una prueba que nunca falla.
Mejore el código en pruebas de forma que supere la nueva prueba:
#include <math.h> ... double CRootFinder::SquareRoot(double v) { double result = v; double diff = v; while (diff > result/1000) { double oldResult = result; result = result - (result*result - v)/(2*result); diff = abs (oldResult - result); } return result; }
Compile la solución y, en el Explorador de pruebas, elija Ejecutar todo.
Ambas pruebas quedan superadas.
Sugerencia Desarrolle código agregando pruebas una a una.Asegúrese de que se pasan todas las pruebas después de cada iteración.
Depurar una prueba fallida
Agregue otra prueba:
#include <stdexcept> ... // Verify that negative inputs throw an exception. TEST_METHOD(NegativeRangeTest) { wchar_t message[200]; CRootFinder rooter; for (double v = -0.1; v > -3.0; v = v - 0.5) { try { // Should raise an exception: double result = rooter.SquareRoot(v); _swprintf(message, L"No exception for input %g", v); Assert::Fail(message, LINE_INFO()); } catch (std::out_of_range ex) { continue; // Correct exception. } catch (...) { _swprintf(message, L"Incorrect exception for %g", v); Assert::Fail(message, LINE_INFO()); } } }
Compile la solución y elija Ejecutar todo.
Abra o haga doble clic en la prueba con errores.
Se resalta el error de aserción.El mensaje de error es visible en el panel de detalles del Explorador de pruebas.
Para ver por qué se produce el error, revise la función:
Establezca un punto de interrupción al principio de la función SquareRoot.
En el menú contextual de la prueba no superada, elija Depurar pruebas seleccionadas.
Cuando la ejecución se detiene en el punto de interrupción, revise paso a paso el código.
Inserte código en la función que está desarrollando:
#include <stdexcept> ... double CRootFinder::SquareRoot(double v) { // Validate parameter: if (v < 0.0) { throw std::out_of_range("Can't do square roots of negatives"); }
Ahora, todas las pruebas pasan.
Refactorizar el código sin cambiar las pruebas
Simplifique el cálculo central en la función SquareRoot:
// old code: // result = result - (result*result - v)/(2*result); // new code: result = (result + v/result)/2.0;
Compile la solución y elija Ejecutar todo para asegurarse de que no se ha introducido un error.
Sugerencia Un buen conjunto de pruebas unitarias le da la seguridad de que no ha introducido errores al cambiar el código.
Mantenga la refactorización separada de los demás cambios.
Pasos siguientes
Aislamiento La mayoría de las DLL dependen de otros subsistemas, como bases de datos y otros archivos DLL.Estos componentes a menudo se desarrollan en paralelo.Para permitir la realización de pruebas unitarias mientras los demás componentes no están disponibles, debe sustituir mock o
Pruebas de comprobación de la compilación. Puede realizar pruebas en el servidor de compilación de su equipo a intervalos establecidos.Esto garantiza que no se producen errores cuando se integra el trabajo de varios miembros del equipo.
Pruebas de protección. Puede exigir que se realicen algunas pruebas antes de que cualquier miembro del equipo proteja código en el control de código fuente.Normalmente, se trata de un subconjunto de las pruebas de comprobación.
También puede asignar un nivel mínimo de cobertura de código.
Vea también
Tareas
Tutorial: Crear y utilizar una biblioteca de vínculos dinámicos (C++)
Conceptos
Pruebas unitarias de aplicaciones C++ existentes con el Explorador de pruebas
Usar Microsoft.VisualStudio.TestTools.CppUnitTestFramework
Otros recursos
Información general sobre la interoperabilidad de código administrado/no administrado