2010-05-01 21 views
12

Comencé a escribir algunas pruebas con el sistema de pruebas unitarias de Qt.Prueba con el módulo QTestLib de Qt

¿Cómo sueles organizar las pruebas? ¿Es una clase de prueba por clase de módulo o prueba todo el módulo con una única clase de prueba? Qt docs sugieren seguir la estrategia anterior.

Quiero escribir pruebas para un módulo. El módulo proporciona solo una clase que va a ser utilizada por el usuario del módulo, pero hay mucha lógica abstraída en otras clases, que también me gustaría probar, además de probar la clase pública.

El problema es que la forma propuesta de Qt para ejecutar las pruebas consistió en la QTEST_MAIN macro:

QTEST_MAIN(TestClass) 
#include "test_class.moc" 

y, finalmente, un programa de prueba es capaz de probar sólo una clase de prueba. Y es una mierda para crear proyectos de prueba para cada clase en el módulo.

Por supuesto, uno podría echar un vistazo a la macro QTEST_MAIN, reescribirla y ejecutar otras clases de prueba. Pero, ¿hay algo que funcione de la caja?

Hasta ahora lo hago con la mano:

#include "one.h" 
#include "two.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication app(argc, argv); 
    TestOne one; 
    QTest::qExec(&one, argc, argv); 
    TestOne two; 
    QTest::qExec(&two, argc, argv); 
} 
+0

Tenga en cuenta que si escribe pruebas como esa, el complemento AutoTest de Qt Creator no las encontrará ni las ejecutará (solo las descubrí) mientras busca las macros "QTEST_MAIN", "QTEST_APPLESS_MAIN" y "QTEST_GUILESS_MAIN". Ver detalles [aquí] (https://gist.github.com/oranja/c032d7f77becca18137d49251e791593). – mBardos

Respuesta

4

Sí, las fuerzas Qtest bit estructura de la prueba extraño y es generalmente inferior a Google comprobará si el sistema/Mock. Por un proyecto que estoy obligado a utilizar QTest (requisito del cliente), y así es como lo uso:

  1. puedo compilar todas las pruebas en conjunto como un proyecto de plantilla subdirectorio
  2. para que la creación de nuevas pruebas más fácil, que comparten una gran cantidad de configuración del proyecto mediante el archivo common.pri que incluyo en cada archivo .pro de prueba
  3. Si es posible, comparto el directorio de archivos del objeto para acelerar la compilación
  4. Ejecuto todos usando un script por lotes + awk + sed.

Configurar estos cuatro puntos es muy fácil y hace que el uso de QTest sea casi agradable. ¿Tiene algún problema con ejecutar varias pruebas que no se resuelven con la configuración descrita anteriormente?

PD: ejecutar las pruebas de la forma en que lo hace, es decir, llamar a QTest :: qExec causa problemas con -o cambio de línea de comando - obtendrá solo resultados para la última clase probada.

5

En nuestra configuración con QTest, hicimos algunas cosas para que sea más agradable.

  • Define una subclase de QObject que se utiliza como una clase base para cualquier nueva clase de prueba de unidad.
  • En el constructor para esa clase, agregamos la instancia de la prueba a una lista estática de pruebas, y en el destructor lo eliminamos.
  • Tenemos una función estática que recorre las pruebas y las ejecuta usando QTest::qExec(). (Acumulamos los valores devueltos cada vez, y lo devolvemos desde nuestra función.)
  • main() llama a esta función y devuelve el resultado como satisfactorio o fallido.
  • Finalmente, en la unidad de compilación de la prueba específica en sí, generalmente incluimos una instancia estática de esa clase.

Esta configuración significa que la clase se creará una instancia antes de ejecutar main(), por lo que se añadirá a la lista de clases para probar cuando principales carreras. El marco requiere que solo necesite heredar su clase correctamente y crear una instancia estática si siempre quiere que se ejecute.

Ocasionalmente también creamos otras pruebas opcionales, que se agregan basadas en los modificadores de línea de comando.

+2

Hola Caleb, luchando con esto todavía. ¿Tiene algún código de ejemplo que estaría dispuesto a compartir? Gracias. –

0

Normalmente organizo pruebas con un ejecutable de prueba por clase bajo prueba.

y, finalmente, un programa de prueba es capaz de probar una sola prueba clase.

Esto es una buena cosa. Aísla sus pruebas entre sí, evitando que cosas como un choque en una prueba bloqueen todas sus otras pruebas. Ese bloqueo podría ser causado por un componente común en varias clases bajo prueba. El patrón de las fallas le indicará el origen subyacente del problema. Básicamente, tiene mejor información de diagnóstico para las fallas si sus pruebas son independientes entre sí.

Facilita la configuración de múltiples ejecutables y ejecuta cada prueba por separado. Use un corredor de prueba para engendrar todos los procesos de prueba.

Actualización:

He cambiado de opinión sobre esto de alguna manera. Una vez que tienes un gran programa con muchas pruebas, vincular cientos de ejecutables de prueba se vuelve muy lento. Mi nueva preferencia es colocar todas las pruebas de una biblioteca en un archivo ejecutable y elegir qué pruebas invocar utilizando los argumentos de la línea de comandos pasados ​​al ejecutable de prueba.

Eso reduce la cantidad de ejecutables de cientos a docenas, pero conserva las ventajas de ejecutar las pruebas por separado.

5

relacionada a la respuesta Publicado por @cjhuitt

Este es un ejemplo que elimina la necesidad de llamar manualmente cada objeto de prueba

Trato de evitar cosas como esta:

MyTestClass1 t1; t1.run(); 
MyTestClass2 t2; t2.run(); 
//etc... 

Mi solución es permitir que los objetos de prueba hereden de una clase base que se agrega a una lista estática El programa principal th es ejecuta todos los objetos de prueba en esa lista. De esa manera, no es necesario cambiar ninguno de los códigos del framework de soporte. Lo único que cambia son las clases de prueba en sí.

Aquí es cómo lo hago:

qtestsuite.h - clase base para la prueba de objetos

#ifndef QTESTSUITE_H 
#define QTESTSUITE_H 

#include <QObject> 
#include <vector> 

class QTestSuite : public QObject 
{ 
    Q_OBJECT 
public: 
    static std::vector<QObject*> m_suites; 

public: 
    explicit QTestSuite(); 

}; 

#endif // QTESTSUITE_H 

qtestsuite.CPP

#include "qtestsuite.h" 
#include <iostream> 

std::vector<QObject*> QTestSuite::m_suites; 

QTestSuite::QTestSuite() : QObject() 
{ 
    m_suites.push_back(this); 
} 

testall.cpp - ejecuta las pruebas

#include "qtestsuite.h" 

#include <QtTest/QtTest> 
#include <iostream> 

int main(int, char**) 
{ 
    int failedSuitesCount = 0; 
    std::vector<QObject*>::iterator iSuite; 
    for (iSuite = QTestSuite::m_suites.begin(); iSuite != QTestSuite::m_suites.end(); iSuite++) 
    { 
     int result = QTest::qExec(*iSuite); 
     if (result != 0) 
     { 
      failedSuitesCount++; 
     } 
    } 
    return failedSuitesCount; 
} 

mytestsuite1.cpp - un objeto de prueba ejemplo, crear más de estos

#include "qtestsuite.h" 

#include <QtTest/QtTest> 

class MyTestSuite1: public QTestSuite 
{ 
    Q_OBJECT 
private slots: 
    void aTestFunction(); 
    void anotherTestFunction(); 
}; 

void MyTestSuite1::aTestFunction() 
{ 
    QString str = "Hello"; 
    QVERIFY(str.toUpper() == "this will fail"); 
} 

void MyTestSuite1::anotherTestFunction() 
{ 
    QString str = "Goodbye"; 
    QVERIFY(str.toUpper() == "GOODBYE"); 
} 

static MyTestSuite1 instance; //This is where this particular test is instantiated, and thus added to the static list of test suites 

#include "mytestsuite1.moc" 

también, a crear el archivo .pro

qmake -project "CONFIG += qtestlib" 
+3

Utilizo este enfoque para varios proyectos de prueba durante dos años, pero ahora me encontré con el siguiente problema de orden de inicialización estático: 'instancia estática de MyTestSuite1' se puede inicializar antes de' m_suites'. Por lo tanto, es mejor tener m_suites como una función que devuelve una referencia a un miembro estático: 'std :: vector & QTestSuite :: mSuites() {static std :: vector INST; devolver INST;} ' – B3ret

Cuestiones relacionadas