Модульное тестирование кода на C++ с Qt Test. Часть 1. Введение
Данное руководство представляет собой введение в модульное тестирование кода на C++ с помощью Qt Test. В нем приводится и подробно анализируется рабочий пример. Предоставляется полный проект qmake и исходный код на C++.
Модульное тестирование кода на C++ с помощью Qt Test
Qt Test – это среда для модульного тестирования кода на C++. Он является частью Qt, что означает, что он включает в себя функции для тестирования графического интерфейса и других элементов Qt, таких как сигналы, но его также можно использовать для тестирования простого (без Qt) кода на C++.
В данном руководстве я собираюсь объяснить, как использовать Qt Test для тестирования класса на C++. Это потребует создания проекта, определения модульного теста и использования различных макросов, доступных для тестирования кода.
Это первый пост из серии, посвященной Qt Test. Посты этой серии:
- Модульное тестирование кода на C++ с помощью Qt Test. Часть1. Введение
- Модульное тестирование кода на C++ с помощью Qt Test. Часть 2. Расширенное тестирование
- Модульное тестирование GUI (графического интерфейса пользователя) с помощью Qt Test. Часть 1. Введение
- Модульное тестирование GUI (графического интерфейса пользователя) с помощью Qt Test. Часть 2. Расширенное тестирование
Настройка проекта
Идея Qt Test заключается в том, что каждый тестовый случай должен быть независимым исполняемым файлом и иметь собственный проект.
Самый быстрый способ создать проект – использовать шаблон «Проект автотестирования» (Auto Test Project), который находится в группе «Другой проект» (Other Project) диалогового окна «Новый проект» (New Project).
Мастер проведет вас через настройку проекта. В частности, в разделе «Подробнее» (Details) можно указать несколько параметров.
«Имя теста» (Test case name) будет именем класса, представляющего юнит-тест.
Если вы не хотите использовать мастер проекта, вам нужно добавить testlib
в переменную QT
в qmake файле проекта:
QT += testlib
Если в вашем модульном тесте нет элементов графического интерфейса, можно отключить модуль gui
:
QT -= gui
Написание модульного теста
Чтобы написать модульный тест кода на C++ с Qt, вам необходимо создать класс, который наследуется от QObject
и реализует, по крайней мере, один частный слот. Каждый частный слот – это независимый тест.
Самый простой класс модульного теста выглядит примерно так:
#include <QtTest>
class TestMinimal : public QObject
{
Q_OBJECT
private slots:
void testFoo();
};
Вы также можете реализовать 4 специальных частных слота, которые используются для операций инициализации и очистки:
// вызывается перед первой тестовой функцией
void initTestCase();
// вызывается перед каждой тестовой функцией
void init();
// вызывается после каждой тестовой функции
void cleanup();
// вызывается после последней тестовой функции
void cleanupTestCase();
Кроме того, у вас в тестовом классе могут быть и другие члены данных и функции, поскольку это обычный класс C++.
Пример класса модульного теста
Для этого руководства я написал простой пример класса для тестирования с именем Calculator
. Этот класс выполняет основные математические операции с 2 целыми числами. Ничего особенного, в конце концов, это всего лишь пример.
Следует иметь в виду, что обычно вы не будете включать код для тестирования непосредственно в проект модульного тестирования, а будете тестировать некоторую библиотеку или код из другого проекта. Поскольку это пример, то мы можем пока всё упростить.
Класс модульного теста:
#include "Calculator.h"
#include <QtTest/QtTest>
class TestCalculator: public QObject
{
Q_OBJECT
private slots:
// -- настройка/очистка --
void init();
// -- тесты --
void testConstructor();
void testSum();
private:
const int A0 = 0;
const int B0 = 0;
private:
Calculator mCalc;
};
Функции класса TestCalculator
будут объяснены в следующих разделах.
Проверка значений
Для проверки того, что во время теста всё работает так, как ожидалось, Qt Test предоставляет различные макросы.
Самая простая проверка, которую вы можете выполнить, – это проверить, истинно ли утверждение. Для этого вы можете использовать макрос QVERIFY
:
void TestCalculator::testConstructor()
{
// значения по умолчанию
Calculator c1;
QVERIFY(c1.getA() == 0);
QVERIFY(c1.getB() == 0);
В этом коде всё хорошо, пока функции getA()
и getB()
возвращают 0. Если это так, при запуске теста вы увидите следующее сообщение:
PASS : TestCalculator::testConstructor()
В случае сбоя сообщение уведомит вас о том, что что-то пошло не так:
FAIL! : TestCalculator::testConstructor() 'c1.getA() == 0' returned FALSE. ()
Loc: [../QtTestIntroduction/TestCalculator.cpp(16)]
Сообщение об ошибке не содержит подробной информации о том, что пошло не так. Оно только говорит вам, какое выражение было ложным.
Если вам нужно добавить к проверке немного контекста, вы можете использовать вторую версию макроса, QVERIFY2
, который позволяет добавить сообщение об ошибке:
// полный конструктор
const int A = 10;
const int B = 2;
Calculator c2(A, B);
QVERIFY2(c2.getA() == A, "first operand doesn't match"); // не совпадает первый операнд
QVERIFY2(c2.getB() == B, "second operand doesn't match"); // не совпадает второй операнд
}
В случае сбоя сообщение отладки добавит дополнительную информацию, переданную в макрос:
FAIL! : TestCalculator::testConstructor() 'c2.getB() == B' returned FALSE. (second operand doesn't match)
Loc: [../QtTestIntroduction/TestCalculator.cpp(25)]
Добавление сообщения об ошибке может показаться лишней работой, но оно поможет людям, не знакомым с вашим кодом. Вы всегда должны предпочесть использование QVERIFY2
(вместо QVERIFY
), чтобы включать в ваши тесты дополнительную информацию.
Qt Test также предоставляет другие версии QVERIFY
, которые предоставляют некоторые дополнительные функции. Например, QTRY_VERIFY_WITH_TIMEOUT
или QVERIFY_EXCEPTION_THROWN
. Первый вариант позволяет проверять условие несколько раз, прежде чем посчитать его ложным. Последний проверяет, выброшено ли конкретное исключение. Для получения дополнительной информации обратитесь к справочной информации в конце данного руководства.
Сравнение значений
Если вам нужна дополнительная информация, когда тест не проходит, и если вы хотите сравнить какое-либо значение, вам понадобится макрос QCOMPARE
:
void TestCalculator::testSum()
{
// sum default
QCOMPARE(mCalc.Sum(), A0 + B0);
Этот макрос будет сравнивать 2 параметра с использованием наиболее подходящего оператора тестирования. Например, для сравнения чисел с плавающей запятой используется функция Qt qFuzzyCompare()
.
Когда QCOMPARE
выдает сбой, он предоставляет более подробную информацию:
FAIL! : TestCalculator::testSum() Compared values are not the same
Actual (mCalc.Sum()): 10
Expected (A0 + B0) : 12
Loc: [../QtTestIntroduction/TestCalculator.cpp(39)]
В этом случае вы также узнаете, каковы возвращаемые и ожидаемые значения. Что во многих случаях очень полезно.
Также QCOMPARE
имеет разные версии, предлагающие дополнительные функции, например QTRY_COMPARE_WITH_TIMEOUT
, который проверяет условие несколько раз, прежде чем считать его ложным. Для получения дополнительной информации обратитесь к справочной информации в конце статьи.
Функция main
приложения
Как упоминалось ранее, каждый юнит-тест должен быть независимым исполняемым файлом. Это означает, что после создания юнит-теста вам понадобится функция main
для его запуска.
Для формирования кода функции main
, в соответствии с вашими потребностями, Qt Test предоставляет 3 макроса:
// полное приложение Qt
QTEST_MAIN(TestName)
// приложение Qt только с core: без GUI, но есть обработка событий
QTEST_GUILESS_MAIN(TestName)
// нет приложения Qt: без GUI и обработки событий
QTEST_APPLESS_MAIN(TestName)
Вы можете добавить один из этих макросов в конец файла cpp, определяющего юнит-тест. В этом примере, поскольку я тестировал простой код на C++ (без Qt), я использовал:
QTEST_APPLESS_MAIN(TestCalculator)
Следует помнить, что если вы объявляете класс юнит-теста непосредственно в файле .cpp (а не в файле .h), вам нужно будет добавить в конце дополнительный #include
:
QTEST_APPLESS_MAIN(TestCalculator)
#include "TestCalculator.moc"
Это требует Qt для правильной работы.
Исходный код
Полный исходный код этого руководства доступен на GitHub.
Справочная информация
Чтобы узнать больше о Qt Test, вы можете ознакомиться с последней документацией по пространству имен QTest.
Заключение
Qt Test – это простой и легкий в использовании фреймворк для написания модульных тестов на C++. Он не предлагает всех функций других подобных фреймворков, но во многих случаях этого может быть достаточно.
Если ваш проект не использует Qt, вам, вероятно, следует выбрать другие варианты. Если же вы работаете с Qt, то Qt Test может быть лучшим выбором, особенно если вы хотите протестировать код графического интерфейса, что я собираюсь обсудить в следующих постах.