Тестирование с использованием Google Test
Задача
Дан линейный порядок и два элемента. Необходимо перечислить элементы линейного порядка, которые находятся между данными, и протестировать решение на правильность и скорость.
Решение
Напишем функцию intersect, которая решает задачу поиска и перечисления с помощью эффективного алгоритма. Также напишем intersect_slow (для удобства — с той же сигнатурой), которая решает ту же задачу медленно, но достоверно.
Пишем два теста: на правильность и на скорость. В обоих генерируется вектор из случайных чисел, на котором запускаются обе функции. В первом случае результаты проверяются на равенство. Во втором подсчитываем время, затраченное обоими алгоритмами, и проверяем, быстрее ли наш эффективный алгоритм.
Реализация
Тестирование будет проводиться в main.cpp.
Подключение gtest к проекту
Добавим в CMakeLists.txt строчку
find_package(GTest)
и изменим строчку target_link_libraries, добавив туда ${GTEST_LIBRARY}, например:
target_link_libraries(${PROJECT_NAME} ${GTEST_LIBRARY})
В main.cpp делаем
#include <gtest/gtest.h>.
Функция main()
Она будет выглядеть так:
int main(int argc, char ** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
Первая строчка позволяет колдовать с разными флагами тестирования. RUN_ALL_TESTS() обязательно должен запускаться ровно один раз, а main должна возвращать его значение.
Собственно тест
Тесты выглядят так:
TEST(test_case_name, test_name) { ... test body ... }
test_case_name — имя тесткейза, test_name — имя теста внутри этого тесткейза. У разных тесткейзов могут быть тесты с одинаковыми именами, ибо полное имя теста составляется из обоих этих имён. Эти имена должны быть валидными плюсовыми идентификаторами. Утверждается, что они не должны содержать в себе подчёркиваний, но с ними всё отлично работает.
Собственно тестирование заключается в вызове, например, EXPECT_EQ(val1, val2), которое проверяет val1 и val2 на равенство. В случае, если они не равны, в выводе будет написано, что случился фейл в таком-то тесте и будут выведены значения val1 и val2. Также бывают EXPECT_LE (проверяет, что val1 <= val2), EXPECT_LT (val1 < val2) и т.д.
Кстати, тесты можно отключать: необходимо исправить test_name на DISABLED_test_name. Тогда этот тест не будет запускаться, а gtest сообщит о том, что имеются отключённые тесты.
Собственно реализация
https://github.com/katyatitkova/intersect_test
Кажущиеся проблемы нашего решения
Каждый тест запускается только один раз, нам этого недостаточно
Вспомним про флаги тестирования, посмотрим в справку по ним:
./intersect --help
(запускать в папке сборки). В частности, узнаем, что флаг --gtest_repeat=[COUNT] решает нашу проблему. Чтобы запуститься с этим флагом, набираем в консоли во всё той же папке сборки, например
./intersect --gtest_repeat=10
А мы вообще сможем повторно запуститься на тех же тестах, на которых завалились? Рандом всё-таки
Да, сгенерённые числа в каждом запуске будут одинаковыми. То есть, если мы вызовем ./intersect --gtest_repeat=10, каждый из этих 10 раз будет разным, но следующий запуск ./intersect --gtest_repeat=10 сгенерирует то же самое, что было в прошлый раз. (Подсказывают, что этот эффект достигается за счёт использования std::default_random_engine)