Возникла задача: запускать тест, только если явно указано, что его нужно запускать. Иными словами пропускать его при обычным прогоне, без параметров.
Тестовый фреймворк GTest.
GTest, как и многие другие имеет возможность фильтровать тесты, иными словами, выбирать только те, которые нужно запустить в данный момент времени:
./tests --gtest_filter=Categoty.some_test
Если фильтр не указывается, то запускаются все тесты. При этом, у Google Test есть макрос, который в рантайме позволяет исключить тест из прогона:
GTEST_SKIP()
Остаётся придумать, как передать условие, по которому этот макрос вызовется.
Дополнительный параметр командной строки заводить не хотелось, тем более, что уже есть фильтр… Оказалось, что достучаться к тому, что передано для --gtest_filter=
в качестве параметра проще простого:
std::string str = ::testing::GTEST_FLAG(filter);
Сама строка фильтра: набор масок, разделённых двоеточием. Собственно, нам достаточно проверить, что фильтр не содержит имени нашего теста и в этом случае скипнуть его:
TEST(Category, some_test) {
std::string str = ::testing::GTEST_FLAG(filter);
auto enable_pos = str.find("Category.some_test");
if (enable_pos == std::string::npos) {
GTEST_SKIP();
}
}
Теперь, тест запустится, только если явно указать имя теста при запуске:
./tests --gtest_filter=Category.some_test
или все тесты, включая наш:
./tests --gtest_filter=Category.some_test:*
Под катом небольшой бонус :)
Бонус
Пытливый взгляд, мог заметить, что наш запустится если в командную строку передать что-то вроде:
./tests --gtest_filter=Category.some_test_super_postfix:*
Ещё, можно заметить, что не обрабатывается -
перед тестом.
По последнему: в этом нет необходимости, потому как это будет обработано самим фреймворком и нашему тесту даже не будет передано управление.
По первому: да. Нужно добавить граничные условия и оформить в виде отдельной функции:
static bool skip_test(std::string_view test_name) {
std::string filter = ::testing::GTEST_FLAG(filter);
auto const pos = filter.find(test_name);
if (pos == std::string::npos)
return true;
auto const end = pos + test_name.length();
if (end < filter.length() && filter[end] != ':')
return true;
if (pos > 0 && filter[pos - 1] != ':')
return true;
return false;
}
TEST(Category, some_test) {
if (skip_test("Category.some_test")) {
GTEST_SKIP();
}
}
Ну и что бы не писать имя теста, добавляем ещё сахара:
static bool skip_this_test() {
std::string full_name =
std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) + '.' +
::testing::UnitTest::GetInstance()->current_test_info()->name();
return skip_test(full_name);
}
TEST(Category, some_test) {
if (skip_this_test()) {
GTEST_SKIP();
}
}
Сдобрить макросами (SKIP_TEST_IF()
или вроде того) по вкусу.
Для чего?
У меня есть один тест, который замеряет производительность. По сути, все тейст кейсы, которые в нём есть, уже проверены, но нужно выполнить какую-то операцию заданное число раз и просто замерить время. Естественно такой тест замедляет выполнение всего набора, да и выполнять его нужно, по сути, один раз (ну или время от времени, что бы проверить отсутствие деградации по скорости).
Как-то так.