unit-testing - tipos - pruebas unitarias java
¿Qué marco de pruebas unitarias debo usar para Qt? (11)
¿Por qué no utilizar el marco de pruebas unitarias incluido en Qt? Un ejemplo: QtTestLib Tutorial .
Estoy comenzando un nuevo proyecto que necesita alguna GUI multiplataforma, y hemos elegido Qt como el GUI-framework.
Necesitamos un marco de pruebas unitarias también. Hasta hace aproximadamente un año, utilizamos un marco interno de pruebas unitarias para proyectos en C ++, pero ahora estamos haciendo la transición al uso de Google Test para nuevos proyectos.
¿Alguien tiene alguna experiencia con el uso de Google Test para aplicaciones Qt? ¿QtTest / QTestLib es una mejor alternativa?
Todavía no estoy seguro de cuánto queremos usar Qt en las partes del proyecto que no son GUI; probablemente preferiríamos simplemente usar STL / Boost en el código central con una pequeña interfaz para la GUI basada en Qt.
EDITAR: Parece que muchos se inclinan hacia QtTest. ¿Alguien tiene alguna experiencia con la integración de esto con un servidor de integración continua? Además, me parece que tener que manejar una aplicación por separado para cada nuevo caso de prueba causaría mucha fricción. ¿Hay alguna buena manera de resolver eso? ¿Qt Creator tiene una buena manera de manejar estos casos de prueba o necesitarías tener un proyecto por caso de prueba?
Comencé a usar QtTest para mi aplicación y muy, muy rápidamente comencé a encontrarme con limitaciones. Los dos problemas principales fueron:
1) Mis pruebas se ejecutan muy rápido, lo suficientemente rápido como para que la sobrecarga de cargar un ejecutable, configurar una aplicación Q (Core) (si es necesario), etc., a menudo empequeñece el tiempo de ejecución de las pruebas. Vincular cada ejecutable también ocupa mucho tiempo.
Los gastos generales siguieron aumentando a medida que se agregaban más clases, y pronto se convirtió en un problema. Uno de los objetivos de las pruebas unitarias es tener una red de seguridad que funcione tan rápido que no sea una carga en absoluto, y esto fue rápidamente no es el caso. La solución es englobar varias suites de prueba en un ejecutable, y mientras (como se muestra arriba) esto es principalmente factible, no es compatible y tiene limitaciones importantes.
2) No soporte de accesorios - un factor decisivo para mí.
Así que después de un tiempo, cambié a Google Test: es un marco de pruebas unitarias mucho más funcional y sofisticado (especialmente cuando se usa con Google Mock) y resuelve 1) y 2), y aún más, puedes usar las prácticas funciones de QTestLib. como QSignalSpy y la simulación de eventos GUI, etc. Fue un poco doloroso cambiar, pero afortunadamente el proyecto no había avanzado demasiado y muchos de los cambios podrían automatizarse.
Personalmente, no utilizaré QtTest sobre Google Test para proyectos futuros, si no ofrezco ventajas reales que pueda ver y tenga inconvenientes importantes.
La unidad probó nuestras bibliotecas usando gtest y QSignalSpy . Usa QSignalSpy para atrapar señales. Puede llamar a las máquinas tragamonedas directamente (como los métodos normales) para probarlas.
No sé que QTestLib es "mejor" que un marco para otro en términos generales. Hay una cosa que hace bien, y es una buena manera de probar aplicaciones basadas en Qt.
Puede integrar QTest en su nueva configuración basada en la Prueba de Google. No lo he probado, pero en función de cómo se diseña QTestLib, parece que no sería demasiado complicado.
Las pruebas escritas con QTestLib puro tienen una opción -xml que puede usar, junto con algunas transformaciones XSLT para convertir al formato necesario para un servidor de integración continua. Sin embargo, mucho de eso depende del servidor de CI con el que vaya. Me imagino que lo mismo se aplica a GTest.
Una sola aplicación de prueba por caso de prueba nunca causó mucha fricción para mí, pero eso depende de tener un sistema de compilación que haría un trabajo decente al administrar la construcción y la ejecución de los casos de prueba.
No conozco nada en Qt Creator que requiera un proyecto separado por caso de prueba, pero podría haber cambiado desde la última vez que miré a Qt Creator.
También sugiero seguir con QtCore y mantenerse alejado de la STL. Utilizar QtCore en todo hará que sea más fácil tratar con los bits de la GUI que requieren los tipos de datos Qt. No tendrá que preocuparse por convertir de un tipo de datos a otro en ese caso.
No tiene que crear aplicaciones de prueba por separado. Simplemente use qExec en una función principal independiente () similar a esta:
int main(int argc, char *argv[])
{
TestClass1 test1;
QTest::qExec(&test1, argc, argv);
TestClass2 test2;
QTest::qExec(&test2, argc, argv);
// ...
return 0;
}
Esto ejecutará todos los métodos de prueba en cada clase en un lote.
Sus archivos .h de clase de prueba se verán de la siguiente manera:
class TestClass1 : public QObject
{
Q_OBJECT
private slots:
void testMethod1();
// ...
}
Desafortunadamente, esta configuración no se describe bien en la documentación de Qt, aunque parece ser bastante útil para muchas personas.
Para agregar a la respuesta de Joe.
Aquí hay un pequeño encabezado que uso (testrunner.h), que contiene una clase de utilidad que genera un bucle de eventos (que es, por ejemplo, necesario para probar conexiones de ranuras de señales en señal de onda y bases de datos) y "ejecuta" clases compatibles con QTest:
#ifndef TESTRUNNER_H
#define TESTRUNNER_H
#include <QList>
#include <QTimer>
#include <QCoreApplication>
#include <QtTest>
class TestRunner: public QObject
{
Q_OBJECT
public:
TestRunner()
: m_overallResult(0)
{}
void addTest(QObject * test) {
test->setParent(this);
m_tests.append(test);
}
bool runTests() {
int argc =0;
char * argv[] = {0};
QCoreApplication app(argc, argv);
QTimer::singleShot(0, this, SLOT(run()) );
app.exec();
return m_overallResult == 0;
}
private slots:
void run() {
doRunTests();
QCoreApplication::instance()->quit();
}
private:
void doRunTests() {
foreach (QObject * test, m_tests) {
m_overallResult|= QTest::qExec(test);
}
}
QList<QObject *> m_tests;
int m_overallResult;
};
#endif // TESTRUNNER_H
Úselo así:
#include "testrunner.h"
#include "..." // header for your QTest compatible class here
#include <QDebug>
int main() {
TestRunner testRunner;
testRunner.addTest(new ...()); //your QTest compatible class here
qDebug() << "Overall result: " << (testRunner.runTests()?"PASS":"FAIL");
return 0;
}
Para el soporte de la herramienta de prueba de prueba de Visual Studio con el marco QtTest use esta extensión de Visual Studio: https://visualstudiogallery.msdn.microsoft.com/cc1fcd27-4e58-4663-951f-fb02d9ff3653
Para extender la solución de mlvljr''s y de Joe, podemos incluso respaldar las opciones completas de QtTest por una clase de prueba y aún ejecutar todo en un lote más el registro:
usage:
help: "TestSuite.exe -help"
run all test classes (with logging): "TestSuite.exe"
print all test classes: "TestSuite.exe -classes"
run one test class with QtTest parameters: "TestSuite.exe testClass [options] [testfunctions[:testdata]]...
Encabezamiento
#ifndef TESTRUNNER_H
#define TESTRUNNER_H
#include <QList>
#include <QTimer>
#include <QCoreApplication>
#include <QtTest>
#include <QStringBuilder>
/*
Taken from https://.com/questions/1524390/what-unit-testing-framework-should-i-use-for-qt
BEWARE: there are some concerns doing so, see https://bugreports.qt.io/browse/QTBUG-23067
*/
class TestRunner : public QObject
{
Q_OBJECT
public:
TestRunner() : m_overallResult(0)
{
QDir dir;
if (!dir.exists(mTestLogFolder))
{
if (!dir.mkdir(mTestLogFolder))
qFatal("Cannot create folder %s", mTestLogFolder);
}
}
void addTest(QObject * test)
{
test->setParent(this);
m_tests.append(test);
}
bool runTests(int argc, char * argv[])
{
QCoreApplication app(argc, argv);
QTimer::singleShot(0, this, SLOT(run()));
app.exec();
return m_overallResult == 0;
}
private slots:
void run()
{
doRunTests();
QCoreApplication::instance()->quit();
}
private:
void doRunTests()
{
// BEWARE: we assume either no command line parameters or evaluate first parameter ourselves
// usage:
// help: "TestSuite.exe -help"
// run all test classes (with logging): "TestSuite.exe"
// print all test classes: "TestSuite.exe -classes"
// run one test class with QtTest parameters: "TestSuite.exe testClass [options] [testfunctions[:testdata]]...
if (QCoreApplication::arguments().size() > 1 && QCoreApplication::arguments()[1] == "-help")
{
qDebug() << "Usage:";
qDebug().noquote() << "run all test classes (with logging):/t/t" << qAppName();
qDebug().noquote() << "print all test classes:/t/t/t/t" << qAppName() << "-classes";
qDebug().noquote() << "run one test class with QtTest parameters:/t" << qAppName() << "testClass [options][testfunctions[:testdata]]...";
qDebug().noquote() << "get more help for running one test class:/t" << qAppName() << "testClass -help";
exit(0);
}
foreach(QObject * test, m_tests)
{
QStringList arguments;
QString testName = test->metaObject()->className();
if (QCoreApplication::arguments().size() > 1)
{
if (QCoreApplication::arguments()[1] == "-classes")
{
// only print test classes
qDebug().noquote() << testName;
continue;
}
else
if (QCoreApplication::arguments()[1] != testName)
{
continue;
}
else
{
arguments = QCoreApplication::arguments();
arguments.removeAt(1);
}
}
else
{
arguments.append(QCoreApplication::arguments()[0]);
// log to console
arguments.append("-o"); arguments.append("-,txt");
// log to file as TXT
arguments.append("-o"); arguments.append(mTestLogFolder % "/" % testName % ".log,txt");
// log to file as XML
arguments.append("-o"); arguments.append(mTestLogFolder % "/" % testName % ".xml,xunitxml");
}
m_overallResult |= QTest::qExec(test, arguments);
}
}
QList<QObject *> m_tests;
int m_overallResult;
const QString mTestLogFolder = "testLogs";
};
#endif // TESTRUNNER_H
propio código
#include "testrunner.h"
#include "test1"
...
#include <QDebug>
int main(int argc, char * argv[])
{
TestRunner testRunner;
//your QTest compatible class here
testRunner.addTest(new Test1);
testRunner.addTest(new Test2);
...
bool pass = testRunner.runTests(argc, argv);
qDebug() << "Overall result: " << (pass ? "PASS" : "FAIL");
return pass?0:1;
}
QtTest es principalmente útil para probar partes que requieren el envío de señal / bucle de evento Qt. Está diseñado de forma tal que cada caso de prueba requiere un archivo ejecutable por separado, por lo que no debe entrar en conflicto con ningún marco de prueba existente utilizado para el resto de la aplicación.
(Por cierto, recomiendo usar QtCore incluso para partes de las aplicaciones que no sean GUI. Es mucho mejor trabajar con él).
Si está utilizando Qt, recomendaría usar QtTest, porque tiene instalaciones para probar la interfaz de usuario y es fácil de usar.
Si usa QtCore, probablemente puede hacerlo sin STL. Con frecuencia encuentro que las clases de Qt son más fáciles de usar que las contrapartes de STL.
Solo he estado jugando con esto. La principal ventaja de usar Google Test sobre QtTest para nosotros es que hacemos todo nuestro desarrollo de IU en Visual Studio. Si usa Visual Studio 2012 e instala el Adaptador de prueba de Google , puede hacer que VS reconozca las pruebas e incluirlas en su Explorador de prueba. Esto es genial para que los desarrolladores puedan usarlo mientras escriben código, y como Google Test es portátil, también podemos agregar las pruebas al final de nuestra compilación de Linux.
Espero que en el futuro alguien agregue soporte para C ++ a una de las herramientas de prueba simultáneas que C # tiene, como NCrunch , Giles y ContinuousTests .
Por supuesto, puede encontrar que alguien escribe otro adaptador para VS2012 que agrega compatibilidad con QtTest al adaptador de prueba, en cuyo caso esta ventaja desaparece. Si alguien está interesado en esto, hay una buena publicación de blog Autoría de un nuevo adaptador de prueba de unidad de estudio visual .