Создание тестов в Qt


Тесты полезно создавать до начала реализации кода. Это позволит вам принаписании теста лучше осмыслить и понять задачу, задав себе вопрос — что нужно сделать для добавления реализации.

Для демонстрации возьмем простой пример: предположим, нам нужно реализовать класс с методами для нахождения максимума и минимума двух чисел. Первая задача заключается в подготовке тестовых данных, которые будут выступать в качестве образцов для тестирования. Возьмем для этой цели четыре пары чисел: (25, 0), (-12, -15), (2007, 2007) и (-12, 5). Теперь, когда тестовые данные готовы, можно начинать писать тесты. Существуют соглашения для названия тестирующего класса и его методов, которые успели закрепиться и зарекомендовать себя на практике с наилучшей стороны. А именно:

  • называйте тестирующий класс именем тестируемого с префиксом Test. Например: если мы тестируем класс MyClass, то тестирующий класс будет называться Test_MyClass;
  • называйте тестовые слоты (методы) именами тестируемых методов.

#include <QtTest>
#include "MyClass.h"

class Test_MyClass : public QObject {
Q_OBJECT
private slots:
    void min();
    void max();
};


void Test_MyClass::min()
{
    MyClass myClass;
    QCOMPARE(myClass.min(25, 0), 0);
    QCOMPARE(myClass.min(-12, -5), -12);
    QCOMPARE(myClass.min(2007, 2007), 2007);
    QCOMPARE(myClass.min(-12, 5), -12);
}

void Test_MyClass::max()
{
    MyClass myClass;
    QCOMPARE(myClass.max(25, 0), 25);
    QCOMPARE(myClass.max(-12, -5), -5);
    QCOMPARE(myClass.max(2007, 2007), 2007);
    QCOMPARE(myClass.max(-12, 5), 5);
}

QTEST_MAIN(Test_MyClass)
#include "test.moc"

В листинге показана программа, которая должна будет проводить тест методов min() и max() класса MyClass. В тестовой программе необходимо включить заголовочный файл QTest. Тестовый класс должен быть унаследован от класса QObject и, для создания специальной метаинформации, содержать в своем определении макрос QObject. Это позволит вызывать слоты класса при исполнении, включая его тестовые слоты в секции private.

Макрос QCOMPARE() принимает два аргумента. Полученный и ожидаемый результат, а затем сравнивает их. Если значения не совпадают, то тогда исполнение тестового метода прерывается сообщением о не пройденном тесте.

Нам нужна функция main о, в которой будет исполняться каждый тест. Ввиду того, что для проведения тестов эта функция выглядит одинаково, Qt предоставляет для ее замены макрос QTEST_MAIN().

В завершение мы должны включить метаинформацию, сгенерированную МОС.

После создания теста можно приступить к реализации методов тестируемого класса.


#ifndef MyClass_h_
#define MyClass_h_

class MyClass {
public:
    int min(int n1, int n2)
    {
        return n1 < n2 ? n1 : n2;
    }

    int max(int n1, int n2)
    {
        return n1 > n2 ? n1 : n2;
    }
};
#endif //MyClass_h_

Класс MyClass реализует два метода для нахождения минимума и максимума.


SOURCES       = test.cpp 
HEADERS       = MyClass.h
CONFIG       += qtestlib
win32:TARGET  = ../TestLib

В pro-файле, в секции config должна быть добавлена опция qttest. Имя заголовочного файла тестируемого класса указано для того, чтобы при любых его изменениях можно было скомпилировать тест заново. При первом проведении теста полезно начать с проверки на отказ, то есть нам нужно модифицировать проверяемый метод так, чтобы тест завершался неудачей. Это поможет нам убедиться в том, что тест действительно выполняется и проверяет то, что требуется. Для этого поменяйте в методах min() и max() класса MyClass знаки сравнения на противоположные. Теперь откомпилируем и запустим тест. На экране появится следующее:


********* Start testing of Test_MyClass *********
Config: Using QTest library 4.7.4, Qt 4.7.4
PASS   : Test_MyClass::initTestCase()
FAIL!  : Test_MyClass::min() Compared values are not the same
   Actual (myClass.min(25, 0)): 25
   Expected (0): 0
   Loc: [../TestLib/test.cpp(24)]
FAIL!  : Test_MyClass::max() Compared values are not the same
   Actual (myClass.max(25, 0)): 0
   Expected (25): 25
   Loc: [../TestLib/test.cpp(34)]
PASS   : Test_MyClass::cleanupTestCase()
Totals: 2 passed, 2 failed, 0 skipped
********* Finished testing of Test_MyClass *********

Методы initTestCase() и cleanupTest() вызываются в начале и конце теста соответственно. Эти методы не трактуются как тест-методы. Они выполняются при запуске тестов и служат для инициализации и очистки теста. Кроме того, на экране мы видим информацию о том, что тест прошел неудачно: сообщение "FAIL!", имена тестов " Test_MyClass::min()" и "Test_MyClass::max()", актуальные значения (Actual) и ожидаемые (Expected). Отлично! Наш тест завершился неудачей, а это значит, что проверка работы теста удалась — он действительно способен отслеживать ошибки. Теперь поменяем операторы сравнения в классе MyClass так, как это показано в листинге. Скомпилируем и запустим тест еще раз. Мы увидим на экране следующие сообщения:


********* Start testing of Test_MyClass *********
Config: Using QTest library 4.7.4, Qt 4.7.4
PASS   : Test_MyClass::initTestCase()
PASS   : Test_MyClass::min()
PASS   : Test_MyClass::max()
PASS   : Test_MyClass::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped
********* Finished testing of Test_MyClass *********

Что говорит о том, что все наши тесты прошли удачно.

Читать далее: Тесты с передачей данных