Share via


Pruebas unitarias de código nativo con el Explorador de pruebas

En Visual Studio, se pueden crear pruebas unitarias para código no administrado escrito en C++.El código no administrado se conoce a veces como código nativo.

El siguiente procedimiento contiene información esencial que le servirá para comenzar.Las secciones posteriores proporcionan un tutorial que describe los pasos con más detalle.

Para escribir pruebas unitarias para un código no administrado DLL

  1. Utilice la plantilla Proyecto de prueba nativo para crear un proyecto independiente de Visual Studio para las pruebas.

    El proyecto contiene algún código de prueba como ejemplo.

  2. Haga el DLL accesible al proyecto de prueba:

    • #include un archivo .h que contiene las declaraciones de las funciones externamente-accesibles del DLL.

      El archivo .h debe contener declaraciones de función marcadas con _declspec(dllimport).Como alternativa, se pueden exportar métodos mediante un archivo DEF.Para obtener más información, vea Importar y exportar.

      Las pruebas unitarias pueden obtener acceso sólo a funciones que se exportan del DLL en pruebas.

    • Agregue el proyecto DLL a las referencias del proyecto de prueba:

      En las Propiedades del proyecto de prueba, expanda Propiedades comunes, Marco de trabajo y referencias y elija Agregar referencia.

  3. En el proyecto de prueba, cree las clases de prueba y los métodos de prueba utilizando las macros de la PRUEBA y validar la clase así:

    #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 varias funciones estáticas que se pueden utilizar para comprobar el resultado de una prueba.

    • El parámetro LINE_INFO() es opcional.En caso de que no haya ningún archivo PDB, permite al ejecutor de pruebas identificar la ubicación de un error.

    • También se pueden escribir métodos de configuración y de limpieza de pruebas.Para obtener más información, abra la definición de la macro TEST_METHOD y lea los comentarios en CppUnitTest.h

    • No se pueden anidar las clases de prueba.

  4. Use el Explorador de pruebas para ejecutar las pruebas:

    1. En el menú Ver, elija Otras ventanas, Explorador de pruebas.

    2. Compile la solución de Visual Studio.

    3. En el explorador de pruebas, elija Ejecutar todas.

    4. Para investigar cualquier prueba con más detalle en el Explorador de pruebas:

      1. Seleccione el nombre de la prueba para ver más detalles, como un mensaje erróneo o un seguimiento de pila.

      2. Abra el nombre de la prueba (por ejemplo, haciendo doble clic) para ir a la ubicación de error o al código de prueba.

      3. En el acceso directo de una prueba, elija Depurar prueba seleccionada para ejecutar pruebas en el depurador.

Tutorial: Desarrollar una DLL no administrada con el Explorador de pruebas

Se puede adaptar este tutorial para desarrollar su propio DLL.A continuación se enumeran los pasos principales:

  1. Crear un proyecto de prueba nativo.Las pruebas se crean en un proyecto de archivo DLL separado al que se está desarrollando.

  2. Crear un proyecto DLLEste tutorial crea un nuevo archivo DLL, pero el procedimiento para probar un DLL existente es similar.

  3. Haga las funciones de archivos DLL visibles a las pruebas.

  4. Aumente iterativamente las pruebas.Se recomienda un ciclo “rojo-verde-refactorizar”, en el que el desarrollo de código es conducido por las pruebas.

  5. Depurar pruebas fallidas.Se pueden ejecutar pruebas en modo de depuración.

  6. Refactorizar conservando las pruebas sin cambios.Refactorizar significa mejorar la estructura del código sin cambiar su comportamiento externo.Se puede hacer para mejorar el rendimiento, la extensibilidad o la legibilidad del código.Dado que la intención no es cambiar el comportamiento, no se cambian las pruebas mientras se efectúa un cambio de refactorización al código.Las pruebas ayudan a garantizar que no se introduzcan errores durante la refactorización.De esta forma, se pueden realizar estos cambios con mucha más confianza que sin tener las pruebas.

  7. Cobertura de comprobación.Las pruebas unitarias son más útiles cuando se ensayan más partes del código.Se puede saber qué partes del código han sido usadas por las pruebas.

  8. Aislar las unidades de recursos externos.Normalmente, un DLL depende de otros componentes del sistema que se está desarrollando, por ejemplo otros archivos DLL, bases de datos o subsistemas remotos.Es útil probar cada unidad por separado sin tener en cuenta las dependencias.Los componentes externos pueden hacer que las pruebas se ejecuten lentamente.Durante el desarrollo, los componentes pueden no estar completos.

Crear un proyecto nativo de prueba unitaria.

  1. en el menú de Archivo , elija Nuevo, proyecto.

    En el cuadro de diálogo, expanda Instalado, Plantillas, Visual C++, Prueba.

    Elija la plantilla Proyecto de prueba nativo.

    En este tutorial, el proyecto de prueba se denomina NativeRooterTest.

    Creación de un proyecto de prueba unitaria de C++

  2. En el nuevo proyecto, inspeccione unittest1.cpp

    Proyecto de prueba con TEST_CLASS y TEST_METHOD

    Observe que:

    • Cada objeto TEST_METHOD(YourTestName){...} está definido por una clase.

      No hay que escribir una firma de función convencional.La firma es creada por la macro TEST_METHOD.La macro genera una función de 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 que el Explorador de pruebas busque el método.

    • Los argumentos se agrupan en clases usando TEST_CLASS(YourClassName){...}.

      Cuando se ejecutan las pruebas, se crea una instancia de cada clase de prueba.A los métodos de prueba se los llama en un orden no especificado.Se pueden 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, consulte Organizar pruebas de C++.

  3. Compruebe que las pruebas se ejecuten en el Explorador de pruebas:

    1. Insertar el código de prueba:

      TEST_METHOD(TestMethod1)
      {
      Assert::AreEqual(1,1);
      }
      

      Observe que la clase Assert proporciona varios métodos estáticos que se pueden utilizar para comprobar los resultados en los métodos de prueba.

    2. En el menú Prueba, elija Ejecutar , Todas las pruebas.

      Se compila y se ejecuta la prueba.

      Aparece el Explorador de pruebas.

      La prueba aparece bajo Pruebas superadas.

      Explorador de pruebas unitarias con una prueba superada

Crear un proyecto DLL no administrado

  1. Cree un proyecto Visual C++ mediante la plantilla Proyecto Win32 .

    En este tutorial, el proyecto se denomina RootFinder.

    Creación de un proyecto Win32 de C++

  2. Seleccione DLL y Exportar símbolos en el Asistente para aplicaciones Win32.

    La opción Exportar símbolos genera una macro práctica que se puede utilizar para declarar métodos exportados.

    Asistente para proyectos de C++ con las opciones de DLL y de exportar símbolos

  3. Declare una función exportada del archivo .h principal:

    Nuevo proyecto de código DLL y archivo .h con macros de API

    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, vea Con dllimport y dllexport en las clases de C++.

  4. 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;
    }
    

Acople el proyecto de prueba al proyecto DLL

  1. Agregue el proyecto DLL a las referencias del proyecto de prueba:

    1. Abra las propiedades del proyecto de prueba y elija Propiedades comunes, Marco de trabajo y referencias.

      Propiedades del proyecto de C++: Marco de trabajo y referencias

    2. Elija Agregar nueva referencia.

      En el cuadro de diálogo Agregar referencia, seleccione el proyecto DLL y elija Agregar.

      Propiedades del proyecto de C++: Agregar nueva referencia

  2. En el archivo .cpp de la prueba unitaria principal, incluya el archivo .h del código DLL:

    #include "..\RootFinder\RootFinder.h"
    
  3. 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());
    }
    
  4. Compile la solución.

    La nueva prueba aparece en el Explorador de pruebas.

  5. En el explorador de pruebas, elija Ejecutar todas.

    Explorador de pruebas unitarias: prueba básica superada

Se han configurado las pruebas y proyectos de código y se ha comprobado que se pueden ejecutar las pruebas que ejecutan funciones en el proyecto de código.Ahora se puede empezar a escribir pruebas y código reales.

Aumente iterativamente las pruebas y haga que pasen

  1. 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);
      }
    }
    
    SugerenciaSugerencia

    Se recomienda no cambiar las pruebas sin errores.En su lugar, agregue una nueva prueba, actualice el código para que la prueba pase y después agregue otras pruebas, y así sucesivamente.

    Cuando los usuarios cambien sus requisitos, deshabilite las pruebas que ya no son correctas.Escriba nuevas pruebas y haga que funcionen de una en una, de la misma manera incremental.

  2. Compile la solución y, en el Explorador de pruebas, elija Ejecutar todas.

    La nueva prueba no se puede realizar.

    Se produce un error RangeTest

    SugerenciaSugerencia

    Compruebe si cada prueba no se puede realizar inmediatamente después de que se ha escrito.Esto ayuda a evitar el error común de escribir una prueba que siempre se puede realizar.

  3. Amplíe el código en pruebas de modo que la nueva prueba pase:

    #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;
    }
    
  4. Compile la solución y, entonces, en el Explorador de pruebas, elija Ejecutar todas.

    Ambas pruebas se completan satisfactoriamente.

    Explorador de pruebas unitarias: prueba de intervalo superada

    SugerenciaSugerencia

    Desarrolle el código agregando pruebas de una en una.Asegúrese de que todas las pruebas pasan después de cada iteración.

Depurar una prueba fallida

  1. Agregar 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());
        }
      }
    }
    
  2. Compile la solución y elija Ejecutar todas.

  3. Abra (o haga doble clic en) la prueba no superada.

    Se resalta el error de aserción.El mensaje de error es visible en el panel de detalles del Explorador de pruebas.

    Se ha producido un error en las pruebas NegativeRangeTests

  4. Para ver por qué dio error, recorra la función:

    1. Establezca un punto de interrupción al principio de la función SquareRoot.

    2. En el acceso directo de la prueba no superada, elija Depurar pruebas seleccionadas.

      Cuando la ejecución se detiene en el punto de interrupción, examine el código.

  5. Inserte el código de la función que se 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");
        }
    
  6. Ahora pasan todas las pruebas.

    Todas las pruebas se realizan correctamente

Refactorizar el código sin cambiar pruebas

  1. 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;
    
  2. Compile la solución y elija Ejecutar todas, para asegurarse de que no se ha introducido un error.

    SugerenciaSugerencia

    Un buen conjunto de pruebas unitarias da confianza de que no se han introducido errores cuando se cambia el código.

    Mantenga la refactorización separada de otros cambios.

Pasos siguientes

  • Aislamiento La mayoría de los DLL dependen de otros subsistemas como las bases de datos y otros archivos DLL.Estos otros componentes a menudo se desarrollan en paralelo.Para permitir que la prueba unitaria se realice mientras otros componentes aún no están disponibles, debe sustituir el ejemplo

  • Pruebas de comprobación de compilación. Se pueden realizar pruebas en el servidor de compilación del equipo a intervalos establecidos.Esto garantiza que los errores no se introduzcan cuando se integra el trabajo de varios miembros del equipo.

  • Pruebas de protección. Se puede ordenar que algunas pruebas se realicen antes de que cada miembro del equipo compruebe el código en el control de código fuente.Normalmente es un subconjunto del conjunto completo de pruebas de comprobación de compilación.

    También se puede ordenar un nivel mínimo de cobertura de código.

Vea también

Tareas

Walkthrough: Creating and Using a Dynamic Link Library (C++)

Conceptos

Importar y exportar

Otros recursos

Información general sobre la interoperabilidad de código administrado/no administrado

Depuración de código nativo