Сегодня вышла новая версия Google Test Framework — 1.3.0.
Радостно, что авторы воплотили мою идею, когда вся библиотека собирается всего в два файла: gtest-all.cc
и gtest.h
. Теперь для этого есть специальный скрипт на Питоне. Распаковываем архив gtest-1.30.zip
и запускаем:
python scripts\fuse_gtest_files.py . fuse
После этого во вновь созданном подкаталоге fuse
будет находиться "упакованная" версия библиотеки в виде двух файлов gtest/gtest-all.cc
и gtest/gtest.h
. Моя аналогичная, но ручная сборка для предыдущей версии больше неактуальна.Опять таки приятно, что включили мой микропатч для возможности установки флагов командной строки прямо в исходниках тестов. Это очень удобно. Например, есть возможность печати времени работы тестов. Но по умолчанию эта функция выключена, и для ее включения надо в командной строке сказать --gtest_print_time
. Неудобно постоянно таскать за собой этот ключ. Теперь же можно прямо в тексте тестов, например, в головном модуле, задать этот параметр:
#include "gtest/gtest.h"
int main(int argc, char* argv[]) {
testing::InitGoogleTest(&argc, argv);
testing::GTEST_FLAG(print_time) = true;
return RUN_ALL_TESTS();
}
Итак, новые возможности версии 1.3.0: - поддержка так называемых смертельных тестов для Windows (раньше это работало только под Linux)
- параметр командной строки
--gtest_also_run_disabled_tests
для принудительного запуска отключенных тестов - возможность распараллеливать запуск тестов на разных машинах
Файл
runner.cpp
: #include "gtest/gtest.h"
#include <fstream>
#include <iostream>
#include <cstdlib>
// -------------------------------------------------------
// Данная функция, если файл не существует, печатает сообщение
// об ошибке и завершает программу с ненулевым кодом.
void openfile(const char* name) {
std::ifstream is(name);
if (!is) {
std::cerr << "Unable to open the file" << std::endl;
std::exit(1);
}
}
// Тест для функции openfile().
TEST(OpenFileDeathTest, ExitIfNoFile) {
// Задаем заведомо несуществующий файл и смотрим - завершилась
// ли программа с ненулевым кодом. Также проверяем регулярным
// выражением то, что программа напечатала при выходе.
// Мы ожидаем слово "open" среди остального вывода.
ASSERT_DEATH({ openfile("__nofile__"); }, ".*open.*");
}
// -------------------------------------------------------
// Данная функция должна падать с assert'ом, если делитель
// равен нулю.
int divide(int a, int b) {
assert(b != 0);
return a / b;
}
// Тест для assert'а в функции divide().
TEST(AssertDeathTest, DivideByZero) {
// Задаем нулевой делитель и смотрим - упала или нет.
// Вывод программы при падении не проверяем.
ASSERT_DEATH({ divide(1, 0); }, "");
}
// -------------------------------------------------------
// Данная функция должна при ненулевом коде завершать
// программу, прибавив к заданному коду ошибки 50.
void abandon(int code) {
if (code != 0) std::exit(code + 50);
}
// Тест для функции abandon().
TEST(AbandonDeathTest, ExitCode) {
// Вызываем функцию и смотрим код возврата.
// Вывод программы при выходе не проверяем.
ASSERT_EXIT(abandon(200), testing::ExitedWithCode(250), "");
}
// -------------------------------------------------------
// Заведомо неработающий “сломанный” тест.
// Если имя группы тестов или теста в отдельности предварить
// словом DISABLED_, то тест не будет участвовать с запуске.
// Это удобно, когда какой-то тест сломан, времени на его
// отладку нет, но убирать его из тестирования совсем нельзя.
// В это случае его можно отключить. Google Test при каждом
// запуске будет напоминать, сколько имеется отключенных тестов.
// В процессе же работы над тестом можно запускать программу
// с параметром "--gtest_also_run_disabled_tests", который
// будет проверять также и отключенные тесты.
TEST(BadTest, DISABLED_Test) {
FAIL();
}
// -------------------------------------------------------
int main(int argc, char* argv[]) {
testing::InitGoogleTest(&argc, argv);
// Принудительно печатаем время работы тестов.
testing::GTEST_FLAG(print_time) = true;
return RUN_ALL_TESTS();
}
Компилируем в Visual Studio: cl /EHsc /I. /Ferunner_vs2008.exe /DWIN32 runner.cpp gtest\gtest-all.cc
Запускаем: [==========] Running 3 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 1 test from OpenFileDeathTest
[ RUN ] OpenFileDeathTest.ExitIfNoFile
[ OK ] OpenFileDeathTest.ExitIfNoFile (31 ms)
[----------] 1 test from OpenFileDeathTest (31 ms total)
[----------] 1 test from AssertDeathTest
[ RUN ] AssertDeathTest.DivideByZero
[ OK ] AssertDeathTest.DivideByZero (31 ms)
[----------] 1 test from AssertDeathTest (31 ms total)
[----------] 1 test from AbandonDeathTest
[ RUN ] AbandonDeathTest.ExitCode
[ OK ] AbandonDeathTest.ExitCode (32 ms)
[----------] 1 test from AbandonDeathTest (32 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 3 test cases ran. (94 ms total)
[ PASSED ] 3 tests.
YOU HAVE 1 DISABLED TEST
Отлично, все работает. Также не забудем, что у нас таки есть один отключенный тест. Его можно запустить принудительно, использовав ключ --gtest_also_run_disabled_tests
: runner_vs2008.exe --gtest_also_run_disabled_tests
Получим следующее: [==========] Running 4 tests from 4 test cases.
[----------] Global test environment set-up.
[----------] 1 test from OpenFileDeathTest
[ RUN ] OpenFileDeathTest.ExitIfNoFile
[ OK ] OpenFileDeathTest.ExitIfNoFile (31 ms)
[----------] 1 test from OpenFileDeathTest (31 ms total)
[----------] 1 test from AssertDeathTest
[ RUN ] AssertDeathTest.DivideByZero
[ OK ] AssertDeathTest.DivideByZero (32 ms)
[----------] 1 test from AssertDeathTest (32 ms total)
[----------] 1 test from AbandonDeathTest
[ RUN ] AbandonDeathTest.ExitCode
[ OK ] AbandonDeathTest.ExitCode (31 ms)
[----------] 1 test from AbandonDeathTest (31 ms total)
[----------] 1 test from BadTest
[ RUN ] BadTest.DISABLED_Test
runner.cpp(72): error: Failed
[ FAILED ] BadTest.DISABLED_Test (0 ms)
[----------] 1 test from BadTest (0 ms total)
[----------] Global test environment tear-down
[==========] 4 tests from 4 test cases ran. (94 ms total)
[ PASSED ] 3 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] BadTest.DISABLED_Test
1 FAILED TEST
Под занавес отмечу, что появился еще один новый ключ командной строки --help
для печати на экран всех весьма многочисленных параметров Google Test. Я уже обновился до версии 1.3.0, а вы?
Вопрос: есть ли в Google Test Framework возможность подмены вывода штатных сообщений о прохождении тестов?
ОтветитьУдалитьНапример, мне нужно тестировать код своей библиотеки, вызываемой из основной программы с недоступным исходным кодом.
Например, разработка внешней компоненты для 1С.
Я немного не понял как связана необходимость подмены штатных сообщений о прохождении тестов и кодом, вызываемым из основной программы с недоступным исходным кодом?
ОтветитьУдалитьЕсли надо изменить вид сообщений типа:
[ RUN ] AbandonDeathTest.ExitCode
[ OK ] AbandonDeathTest.ExitCode (31 ms)
на
AbandonDeathTest.ExitCode: Running...
AbandonDeathTest.ExitCode: OK (31 ms)
и что-то в этом роде, то скорее всего придется просто изменить исходники библиотеки, благо они доступны.
А если вам надо тестировать что-то, вызываемое извне, то может имеет смысл замутить какой-нибудь симулятор для "как бы подконтрольного" вызова вашего когда.
Да, задача состоит именно в том, чтобы запустить тестирование своего кода, вызываемого извне (своя библиотека/плагин, вызываемая из чужого приложения), и не в функции main().
ОтветитьУдалитьКаким образом в GTF можно решить эту задачу?
Сходу я универсального решения не могу придумать.
ОтветитьУдалитьА на "той" стороне (1С) у вас тоже специальная тестовая среда, которая вызывает для тестов ваш плагин?
"есть ли в Google Test Framework возможность подмены вывода штатных сообщений о прохождении тестов?"
ОтветитьУдалитьНе нашел, в отличие кстати, от того же boost.test или CppUnit.
Это мегаудабная фича для интеграции с подсистемами сборки. Можно написать свой листенер, который слегка подправляет или меняет сообщения об успешном прохождении тестов. Это единственное что нас сдерживает от перехода на этот фреймворк с CppUnit.
Вот например как это делается для TeamCity http://www.jetbrains.net/confluence/display/TCD4/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingTests
http://www.jetbrains.net/confluence/display/TW/Cpp+Unit+Test+Reporting
Я может чего-то упускаю, но какая разница, как именно печатаеся вообщение об ошибке? Ведь неудачный unit тест - это причина полной остановки сборки, и суть репорта - это фактически "да" или "нет", собрал или не собрал. Я еще понимаю в QA тестах можно иметь при исправной сборке список сломаных тестов и работать над сокращением данного списка. Вот тут уже нужно иметь удобную систему отчетов, генерируемую по результатам прохода тестов. Но чем это полезно в unit-тестировании?
ОтветитьУдалитьРазница в том, чтобы сторонняя система автоматизированной сборки смогла разобрать какой именно тест был неудачен, таким образом мы сразу видим источник проблемы, без дополнительных прогонов и расследований (если конечно имя теста вразумительное). Кроме того, это нужно для статистики проекта - когда, сколько тестов, сколько времени и т.д. Очень полезно при анализе тенденций проекта.
ОтветитьУдалитьВ целом, конечно удобно, когда можно настраивать вывод под себя, например, через шаблоны или плагины. И неплохо было бы иметь это в Google Test. Надо будет почитать, может у гугловцев это уже в планах.
ОтветитьУдалитьНо с другой стороны можно подкрутить и стороннюю систему автоматизированной сборки для разбора вывода Google Test'а.
Имхо, во-первых парсинга и не надо для юнит тестов.
ОтветитьУдалитьА во-вторых, согласан с Александром, gTest настолько прост и исходники есть - сделать любой свой формат вывода - раз плюнуть. И шаблоны и листенеры никакие не нужны :)
Объсните пожалуйста, что такое "так называемые смертельные тесты"?
ОтветитьУдалитьДостаточно линка на маны в качестве пояснения. Спасибо.
chester: http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Death_Tests
ОтветитьУдалитьВкратце: "смертельный" тест позволяет выяснить код завершения фрагмента программы. Удобно для тестирования кодов возврата (например, разбор командной строки и аварийного выхода, если есть ошибки в ней) или срабатывания assert'ов.
А как подцепить GTF в проект NetBeans?
ОтветитьУдалитьПросто добавляете gtest-all.cc и gtest.h в проект и все.
ОтветитьУдалить