2.03.2008

Test unitarios en C++: guía rápida

Propongo un contexto real: estamos programando bajo una plataforma asquerosa difícil de trata, pongamos una PDA. Tenemos un módulo, por ejemplo una clase que carga un fichero de configuración, que queremos probar y no queremos crear un programa específico para PDA que use la clase, cargue un fichero que hemos tenido que copiar y luego muestre resultados. Es mucho más cómodo hacerlo haciendo un "make" desde la línea de comandos.

Hay diferentes frameworks para hacer test unitarios en C++, cada uno con sus ventajas, se puede ver un análisis (bastante pragmático) en games from whitin. Yo he escogido cppunitlite, por las siguientes razones:

- tiene todo lo que necesito: fixtures, asserts
- no tiene dependencias, se compila con un solo make y a funcionar
- apenas son 10 ficheros

Una vez bajado para arrancar a funcionar solo es necesario tener instalado un compilador. Para windows me gusta usar Mingw ya que además trae make. Además conviene tener instaladas las herramientas de línea de comandos de linux para windows (si no las tienes intaladas ya mereces un castigo).

Bueno, vamos a crear el primer test unitario (formateo patrocinado por el conversor a HTML de vim):
//
#define GLOBAL_CONFIGURATION "configuration.txt"
#define DEBUG_INFO
#include "lib/TestHarness.h"
#include "../../src/configuration.h"


class ConfigurationFixtureSetup : public TestSetup
{
public:
    void setup()
    {
        entries.push_back(conf_entry("LOG_PROY_DATA",0));
        entries.push_back(conf_entry("GGA",1));
        entries.push_back(conf_entry("GGL",1));
        entries.push_back(conf_entry("RMC",1));
        entries.push_back(conf_entry("RMZ",1));
        entries.push_back(conf_entry("VTG",0));
        entries.push_back(conf_entry("GSA",1));
        entries.push_back(conf_entry("LOG_REAL_DATA",0));
        entries.push_back(conf_entry("SMOOTH_DIR",0));
        entries.push_back(conf_entry("RATE",10));
        entries.push_back(conf_entry("THRESHOLD_VEL",0));
        entries.push_back(conf_entry("SHOW_CALC_DIR",0));
        entries.push_back(conf_entry("SHOW_VELOCITY",0));
        entries.push_back(conf_entry("SHOW_SAT_INFO",1));

    }

    void teardown()
    {
        entries.resize(0);
    }

    
protected:
    Configuration fixture;
    struct conf_entry
    {
        conf_entry(const char *n, int v):
        varName(n),
        value(v)
        {}
        const char* varName;
        int value;
    };
    std::vector<conf_entry> entries;
};



/**
 */
TESTWITHSETUP (ConfigurationFixture,Test_ReadConf)
{

    for(int i = 0; i <  entries.size(); ++i)
    {
        int value;
        CHECK(Configuration::GlobalConfiguration()->getValue(entries[i].varName, value));
        printf("%s -> %d\n",entries[i].varName, value);
        CHECK( value == entries[i].value);
        
    }

}


En resumen lo que hace es cargar un fichero de configuración y testear que todos los valores cargados son los esperados.

Es un test muy simple y se puede lanzar desde la línea de comandos después (o antes) de la compilación sin necesidad de meterlo en la PDA, que es un coñazo terrible.



Si de verdad quieres saber un detalle como hacer test unitarios en C++ recomiendo que leas Pruebas unitarias con C++, extenso y completo artículo de como hacer test en C++ con CPP unit escrito pon JM, un compañero de Unkasoft.

2 comentarios:

Anónimo dijo...

Usar printf bueno... pero no usar std::string? Por que etherné, por queee! :P

Anónimo dijo...

Tiene narices que buscando info de unittests en C++ para un compañero haya acabado aquí...

Thanks Javi!