Как собрать результаты всех модульных тестов QtTestLib в один файл при использовании одного тестового проекта?
В нашем проекте мы используем QtTestLib для модульного тестирования. Причины в том, что весь проект уже использует Qt, когда это возможно, и это приложение с графическим интерфейсом, поэтому мы хотели иметь возможность тестировать интерфейсы с графическим интерфейсом.
Наш проект скомпилирован MSVC, поэтому мы не хотели иметь отдельный файл проекта для каждого теста, потому что это будет загромождать решение. Таким образом, мы создали единый проект для всех тестов. Все тестирование должно быть автоматизировано в CIS (непрерывная интеграция), поэтому мы попытались подключить наши тесты к Hudson через выходной файл в формате XML, используя некоторые преобразования XSLT.
Но, похоже, проблема с выводом тестов. Если вы используете один main() для всех тестов и просто передаете аргументы строки cmd в каждый тест:
#include "MyFirstTest.h"
#include "MySecondTest.h"
int main(int argc, char **argv)
{
int result = 0;
MyFirstTest test1;
result |= QTest::qExec(&test1, argc, argv);
MySecondTest test2;
result |= QTest::qExec(&test2, argc, argv);
return result;
}
тогда вы получите файл результатов, переписанный несколько раз. Так что если вы хотите автоматизировать его с помощью выходного файла (например, xml), вы получите только последний результат. Все остальное будет перезаписано.
Мы уже попробовали этот подход, он не дает вам возможности использовать некоторые системы непрерывной интеграции, такие как Hudson. Поэтому мой вопрос будет: есть ли возможность добавить результаты в один выходной файл? Конечно, мы можем использовать некоторые обходные пути, такие как запуск каждого теста с помощью QTest::qExec() с измененными параметрами, для записи результатов в отдельные файлы, но это не лучший способ. В идеале я хочу иметь один файл результатов, чтобы использовать его с CIS.
4 ответа
С помощью этого трюка вы можете собирать отдельные тестовые отчеты xml во временные буферы / файлы; все из одного тестового двоичного файла. Позволяет использовать QProcess для сбора отдельных результатов теста из одного двоичного файла; тест вызывает себя с измененными аргументами. Во-первых, мы вводим специальный аргумент командной строки, который использует правильные подтесты - все это все еще в вашем тестовом исполняемом файле. Для нашего удобства мы используем перегруженную функцию qExec, которая принимает QStringList. Тогда мы можем легче вставить / удалить наш аргумент "-subtest".
// Source code of "Test"
int
main( int argc, char** argv )
{
int result = 0;
// The trick is to remove that argument before qExec can see it; As qExec could be
// picky about an unknown argument, we have to filter the helper
// argument (below called -subtest) from argc/argc;
QStringList args;
for( int i=0; i < argc; i++ )
{
args << argv[i];
}
// Only call tests when -subtest argument is given; that will usually
// only happen through callSubtestAndStoreStdout
// find and filter our -subtest argument
size_t pos = args.indexOf( "-subtest" );
QString subtestName;
if( (-1 != pos) && (pos + 1 < args.length()) )
{
subtestName = args.at( pos+1 );
// remove our special arg, as qExec likely confuses them with test methods
args.removeAt( pos );
args.removeAt( pos );
if( subtestName == "test1" )
{
MyFirstTest test1;
result |= QTest::qExec(&test1, args);
}
if( subtestName == "test2" )
{
MySecondTest test2;
result |= QTest::qExec(&test2, args);
}
return result;
}
В вашем скрипте / вызове командной строки:
./Test -subtest test1 -xml ... >test1.xml
./Test -subtest test2 -xml ... >test2.xml
и вот вы здесь - у нас есть средства для разделения результатов тестов. Теперь мы можем продолжать использовать возможность QProcess для сбора stdout для вас. Просто добавьте эти строки в свою главную. Идея состоит в том, чтобы снова вызвать наш исполняемый файл, если не требуется никаких явных тестов, но с нашим специальным аргументом:
bool
callSubtestAndStoreStdout(const String& subtestId, const String& fileNameTestXml, QStringList args)
{
QProcess proc;
args.pop_front();
args.push_front( subtestId );
args.push_front( "-subtest" );
proc.setStandardOutputFile( fileNameTestXml );
proc.start( "./Test", args );
return proc.waitForFinished( 30000 ); // int msecs
}
int
main( int argc, char** argv )
{
.. copy code from main in box above..
callSubtestAndStoreStdout("test1", "test1.xml", args);
callSubtestAndStoreStdout("test2", "test2.xml", args);
// ie. insert your code here to join the xml files to a single report
return result;
}
Затем в вашем скрипте / вызове командной строки:
./Test -xml # will generate test1.xml, test2.xml
Действительно, надеюсь, что будущие версии QTestLib сделают это проще.
Я использовал этот грязный обходной путь (работает с Дженкинсом):
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int result = 0;
freopen("MyAppTests_Test1.xml", "w", stdout);
result |= QTest::qExec(new Test1, argc, argv);
freopen("MyAppTests_Test2.xml", "w", stdout);
result |= QTest::qExec(new Test2, argc, argv);
return result;
}
Затем в Jenkins я добавил действие сборки "execute shell": ./path_to_MyAppTests -xml
и добавил Действия после сборки "опубликовать отчет о результатах теста xUnit" (QTestlib). Шаблон QTestlib: MyAppTests *.xml
На мой взгляд, попытка создать один исполняемый файл - плохая идея: если один из ваших тестов завершится сбоем, другие больше не будут выполняться...
Еще один способ запустить набор с несколькими тестовыми сценариями:
- Создайте проект subdirs на верхнем уровне.
- добавьте подпапку с собственным.pro для каждого тестового случая и добавьте это в проект subdirs.
- собрать проект из папки верхнего уровня
- бежать
make check
на верхнем уровне make-файла. Это вызовет все ваши тестовые случаи. Вы также можете передать параметры, например, использоватьnmake -k check TESTARGS="-o result.xml,xml -v2 -maxwarnings 0"
с вашей средой MSVC. Ключ -k помогает продолжить работу, если один тест не пройден. - Плагин xunit Jenkins в качестве примера позволяет шаблон
my_build\*\result.xml
для поиска ваших XML-файлов, и таким образом вы можете анализировать все сгенерированные файлы без объединения в один файл.
Поскольку я пока не могу комментировать здесь, я опубликую это здесь в дополнении к ответу muenalan. Есть несколько исправлений, которые нужно применить для его работы (по крайней мере, с Qt5):
В callSubtestAndStoreStdout есть 3 ошибки. Во-первых, первый аргумент должен быть извлечен спереди (это аргумент 0), прежде чем выдвигать новые. Во-вторых, вы должны перенаправить вывод перед началом процесса. В-третьих, он должен вернуть какое-то значение;)
QProcess proc; args.pop_front(); args.push_front(subtestId); args.push_front("-subtest"); proc.setStandardOutputFile(fileNameTestXml); proc.start("sportSystemTest.exe", args); return proc.waitForFinished(30000);
Основное также имеет некоторые (очевидные) ошибки. Основным из них является утверждение if:
if ((-1 != pos) && (pos + 1 < args.length()))
как оригинал никогда не будет стрелять.
В любом случае, спасибо за решение, это решило мою большую головную боль:)