§ 2. Модульное тестирование

Модульное тестирование

01Частью любого проекта является набор тестов, которые запускаются после каждой сборки для проверки корректности кода. Как правило, запуск тестов выполняется автоматически после загрузки кода на центральный сервер, а результаты тестов отображаются на веб-интерфейсе сервера для ознакомления. Если тесты проходят успешно, код отправляется на проверку лидеру команды, и после этого сливается с основной веткой кода. Мы рассмотрим только первый шаг этого процесса — написание и запуск тестов.

Модульные тесты — это обычные программы на C++. Тест считается пройденным успешно, если функция main возвращает 0. Тест пропускается, если main возвращает 77 (это число досталось в наследство от старых систем). В любом другом случае тест завершается ошибкой.

Для создания тестов доступно большое количество библиотек, одной из которых является Google Test. Файлы с тестами пишутся в декларативном стиле с помощью макросов. Макросы построены на сравнении ожидаемогои актуальногозначений. В сравнении могут участвовать любые типы, для которых определен оператор сравнения. Пример теста приведен ниже.

#include <gtest/gtest.h> // заголовочный файл Google Test
#include <algorithm>
#include <cmath>

TEST(Max, Compare) {
    EXPECT_EQ(10, std::max(10,1)); // ожидается 10
    EXPECT_EQ(1, std::max(0,1)); // ожидается 1
    EXPECT_EQ(1.0/0.0, std::max(1.0/0.0,-1.0/0.0)); // Inf
    EXPECT_TRUE(std::isnan(std::max(0.0/0.0,1.0/0.0))); // NaN
    EXPECT_TRUE(std::isnan(std::max(0.0/0.0,-1.0/0.0))); // NaN
}

В тесте производится проверка функции std::max. Функция main в тестах отсутствует, поскольку предоставляется самой библиотекой gtest.

Для того чтобы Meson создал модульный тест, необходимо обернуть команду executable в команду test и подключить библиотеку gtest. Сами тесты, как правило, хранятся в отдельной директории, например, в src/test (общая структура директорий описана в предыдущем задании). Поскольку тесты модульные, то каждый из них проверяет работу отдельной функции или класса и их название совпадает с проверяемой функцией или классом. Пример конфигурации приведен ниже.

gtest = dependency('gtest', main: true)
test(
    'myclass',
    executable(
        'myclass_test',
        sources: ['myclass_test.cc'],
        include_directories: src,
        dependencies: [gtest]
    )
)

Флаг main позволяет использовать или не использовать встроенную функцию main. После этого все тесты можно запустить командой meson test или ninja test.

Задания

02При выполнении заданий должны быть соблюдены следующие условия.

  1. Создайте простой тест для каждого из следующих методов и конструкторов класса std::vector:
    • push_back,
    • pop_back,
    • erase,
    • begin и end,
    • size,
    • vector(const vector&),
    • vector(vector&&),
    • vector& operator=(const vector&),
    • vector& operator=(vector&&).
    Пример проверки метода size:
    TEST(vector, size) {
        std::vector<float> x;
        EXPECT_EQ(0u, x.size());
        x.push_back(1);
        EXPECT_EQ(1u, x.size());
        // ...
    }
    
  2. Создайте параметризованный тест для проверки метода erase. Параметром теста должна быть следующая структура.
    template <class T>
    struct Erase_params {
        // исходные элементы
        std::vector<T> elements;
        // начальный индекс, с которого начинается удаление элементов
        size_t start_index;
        // конечный индекс, на котором заканчивается удаление элементов
        size_t end_index;   
        // элементы, которые останутся в векторе после удаления
        std::vector<T> result;
    };
    
  3. Создайте типизированный тест для проверки метода push_back. Параметром теста является тип элемента вектора. Используйте следующие типы в качестве параметра: float, std::ofstream, std::string.

Ссылки