Google Test + Visual Studio

Введение

Фреймворк тестирования от Google (GTest) основан на архитектуре xUnit. Такие архитектуры имеют следующие базовые компоненты:

  • модуль, выполняющий тестирование (Test runner);
  • тестовые сценарии (Test cases);
  • конфигурации тестирования (Test fixtures);
  • наборы тестов (Test suites);
  • выполнение тестов (Test execution);
  • форматирование результатов тестирования (Test result formatter);
  • утверждения (Assertions).

GTest является кросплатформенной системой, поддерживающей автоматическое обнаружение тестов. Это значит, что пользователю не нужно вручную перечислять все тесты в тестовом наборе. GTest поддерживает множество утверждений, таких как фатальные утверждения (с префиксом ASSERT_), нефатальные утверждения (с префиксом EXPECT_), и тесты “смерти”, проверяющие, что программа завершит свою работу ожидаемым образом.

Об элементарных принципах GTest можно почитать здесь.

GTest поддерживает различные опции запуска тестов и предоставляет возможность сохранения результатов тестов в текстовом виде и в виде XML-отчёта.

Также GTest поддерживает тестирование с использованием mock-объектов, т.е. объектов, преставляющих собой фиктивную реализацию некоторого интерфейса (заглушку) - Google Mock.

Чтобы начать использование GTest необходимо выполнить следующие пункты:

  • Скачать Google test.
  • Скомпилировать Google test в статическую библиотеку.
  • Создать проект unit-теста.
  • Создать тестовые сценарии.

0. После введения

Для начала, предположим, что у нас есть реализация шаблонного класса стека с фиксированным размером – класс FxStack. Объявление класса FxStack следующее:

template<typename Type, int size_>
class FxStack {
public:
    bool empty() const;
    bool full() const;
    Type pop();
    void push(Type val);
    int size() const;
    int sp() const;
    const Type& top() const;
    Type& top();

private:
    Type data_[size_];
    int sp_ = -1;
};

В дальнейшем именно методы класса FxStack будут тестироваться с помощью GTest.

1. Получение GTest

Прежде чем начать работу с GTest его необходимо загрузить. Можно клонировать репозиторий или загрузить его в виде zip-архива:

Исходный код фреймворка GTest расположен в подпапке googletest/. Разархивируем папку googletest/, например, в корень диска C. В дальнейшей работе нам необходимо будет содержимое подпапок include/ и src/:

В подпапке src/ находятся файлы исходного кода фреймворка GTest, а в include/ – заголовочные файлы.

2. Компиляция GTest в статическую библиотеку

На данный момент у нас есть “решение” SomeSolution с проектом реализующим шаблонный класс FxStack, методы которого необходимо будет тестировать:

Добавим к SomeSolution новый проект, компилируемый в статическую библиотеку, который будет служить для создания статической библиотеки GTest. Выполним следующую последовательность действий через меню VS:

  • File -> Add -> New Project.
  • Выбераем шаблон Win32 -> Win32 Console Application.
  • Задаём имя проекта, например, gtestlib. Жмём “OK”.
  • В открывшемся модальном диалоговом окне жмём Next и выбираем тип приложения “Static library”, снимаем галочку “Precompiled header”. Жмём “Finish”.

Получаем следующую структуру “решения”:

Заходим в свойства проекта gtestlib через его контекстное меню, нажав RMB на имени проекта и выбрав “Properties”. В настройках переходим на закладку “VC++ Directories” и задаём новые значения в поле “Include Dictionaries”: путь к корневой папке фреймвока GTest (C:\googletest) и к его подпапке include/ (C:\googletest\include).

Также, через контекстное меню проекта gtestlib, Project -> Add Existing Item, добавляем в него файлы исходного кода фреймворка GTest: gtest_all.cc и gtest_main.cc, находящиеся в вышеупомянутой подпапке src/ (C:\googletest\src).

Делаем сборку проекта gtestlib в статическую библиотеку (команда Build):

3. Создание проекта unit-теста

Добавим к решению SomeSolution проект Win32 Console Application, например, с именем unittest_FxStack, используя меню, File -> Add -> New project. При создании проекта в дополнительных опциях настроек проекта установим галочку “Empty project”.

Теперь необходимо в настройках проекта добавить пути к корневой директории фреймворка GTest, к её подпапке include, а также к папке с исходными/заголовочными файлами тестируемого проекта:

К проекту unittest_FxStack добавим ссылки на проекты gtestlib и FxStack. – Через контекстное меню проекта unittest_FxStack переходим по Add -> Reference…, и выбираем необходимые проекты:

В итоге в проекте unittest_FxStack отобразятся добавленные зависимости:

4. Создание тестовых сценариев

В проект unittest_FxStack необходимо добавить файл исходных кодов (cpp-файл) в котором будут размещены тестовые сценарии, например, файл FxStackTestCases.cpp:

Первое, что нужно сделать в созданном файле – подключить заголовочный файл тестируемой сущности, в данном случае заголовочный файл шаблоного класса стека с фиксированным размером. Второе, – подключить заголовочный файл фреймворка GTest:

#include <FxStack.h>

#include <gtest/gtest.h>

Далее очередь за написанием тестовых сценариев. Синтаксис сценариев следующий:

TEST(имя_тестового_сценария, имя_теста)
{
	/* тело_сценария */
}

Имя тестового сценария и имя теста могут быть произвольными.

Чтобы протестировать метод size() класса FxStack, можно создать следующий сценарий:

TEST(FxStack, Size)
{
    FxStack<int, 3> stack;

    EXPECT_EQ(stack.size(), 3);
}

Для тестирования методов push() и pop(), можно создать сценарии следующего содержания:

TEST(FxStack, Push)
{
    FxStack<int, 3> stack;

    EXPECT_NO_FATAL_FAILURE(stack.push(11));
    EXPECT_NO_FATAL_FAILURE(stack.push(22));
    EXPECT_NO_FATAL_FAILURE(stack.push(33));
    EXPECT_THROW(stack.push(44), std::overflow_error);
}

TEST(FxStack, Pop)
{
    FxStack<int, 3> stack;
    stack.push(11);
    stack.push(22);
    stack.push(33);

    EXPECT_EQ(stack.pop(), 33);
    EXPECT_EQ(stack.pop(), 22);
    EXPECT_EQ(stack.pop(), 11);
    EXPECT_THROW(stack.pop(), std::underflow_error);
}

Для методов проверки пустоты и заполненности стека (соответственно, методы empty() и full()), можно применить такие тестовые сценарии:

TEST(FxStack, Empty)
{
    FxStack<int, 3> stack;

    EXPECT_TRUE(stack.empty());
}

TEST(FxStack, Full)
{
    FxStack<int, 3> stack;
    stack.push(11);
    stack.push(22);
    stack.push(33);

    EXPECT_TRUE(stack.full());
}

Проверку корректности метода top() – метода доступа к последнему элементу без удаления из стека, можно сделать следующим образом:

TEST(FxStack, Top)
{
    FxStack<int, 3> stack;

    stack.push(11);
    EXPECT_EQ(stack.top(), 11);

    stack.push(22);
    EXPECT_EQ(stack.top(), 22);

    EXPECT_NO_FATAL_FAILURE(stack.top() = 33);
    EXPECT_EQ(stack.top(), 33);
}

И, наконец, метод получения индекса вершины стека - sp(), используемого для проверки величины заполненности стека, можно протестировать так:

TEST(FxStack, StackPointer)
{
    FxStack<int, 3> stack;

    EXPECT_EQ(stack.sp(), -1);

    stack.push(11);
    EXPECT_EQ(stack.sp(), 0);

    stack.push(22);
    EXPECT_EQ(stack.sp(), 1);
}

Напомним, что все тестовые сценарии располагаются в файле FxStackTestCases.cpp. Теперь необходимо собрать (Build) проект unittest_FxStack и запустить созданный exe-файл. Результат его выполнения, в данном случае, получится следующим:

Видно, какой тест был запушен, какой тест был успешно выполнен и общий статус успешности прохождения тестового сценария.

По мотивам статьи.