Раздатка Модульное тестирование кода на C легкое. Настройка проекта
Скачать 48.4 Kb.
|
Модульное тестирование кода на C++ с помощью Qt Test Настройка проектаИдея Qt Test заключается в том, что каждый тестовый случай должен быть независимым исполняемым файлом и иметь собственный проект. Самый быстрый способ создать проект – использовать шаблон «Проект автотестирования» (Auto Test Project), который находится в группе «Другой проект» (Other Project) диалогового окна «Новый проект» (New Project). Рисунок 1 – Создание проекта автоматического тестирования в Qt Creator для модульного тестирования кода на C++ с помощью Qt Test Мастер проведет вас через настройку проекта. В частности, в разделе «Подробнее» (Details) можно указать несколько параметров. Рисунок 2 – Детали настройки проекта автотестирования в Qt Creator «Имя теста» (Test case name) будет именем класса, представляющего юнит-тест. Если вы не хотите использовать мастер проекта, вам нужно добавить testlib в переменную QT в qmake файле проекта: QT += testlib Если в вашем модульном тесте нет элементов графического интерфейса, можно отключить модуль gui: QT -= gui Написание модульного теста Чтобы написать модульный тест кода на C++ с Qt, вам необходимо создать класс, который наследуется от QObject и реализует, по крайней мере, один частный слот. Каждый частный слот – это независимый тест. Самый простой класс модульного теста выглядит примерно так: #include class TestMinimal : public QObject { Q_OBJECT private slots: void testFoo(); }; Вы также можете реализовать 4 специальных частных слота, которые используются для операций инициализации и очистки: // вызывается перед первой тестовой функцией void initTestCase(); // вызывается перед каждой тестовой функцией void init(); // вызывается после каждой тестовой функции void cleanup(); // вызывается после последней тестовой функции void cleanupTestCase(); Кроме того, у вас в тестовом классе могут быть и другие члены данных и функции, поскольку это обычный класс C++. Пример класса модульного тестаКласс модульного теста: #include "Calculator.h" #include 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 для правильной работы. Полный исходный текст программы: calculator.h #ifndef CALCULATOR_H #define CALCULATOR_H #pragma once // example class to test class Calculator { public: Calculator(int a = 0, int b = 0) : mA(a), mB(b) { } int GetA() const { return mA; } void SetA(int a) { mA = a; } int GetB() const { return mB; } void SetB(int b) { mB = b; } int Sum() const { return mA + mB; } int Diff() const { return mA - mB; } int Mult() const { return mA * mB; } int Div() const { return mA / mB; } private: int mA; int mB; }; #endif // CALCULATOR_H qt_test_introduction.pro QT += testlib QT -= gui CONFIG += qt console warn_on depend_includepath testcase CONFIG -= app_bundle TEMPLATE = app SOURCES += tst_testcalculator.cpp HEADERS += \ calculator.h \ tst_testcalculator.h tst_testcalculator.cpp #include "tst_testcalculator.h" // -- setup/cleanup -- void TestCalculator::init() { mCalc.SetA(A0); mCalc.SetB(B0); } // -- tests -- void TestCalculator::testConstructor() { // default values Calculator c1; QVERIFY(c1.GetA() == 0); QVERIFY(c1.GetB() == 0); // full constructor 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"); } void TestCalculator::testSum() { // sum default QCOMPARE(mCalc.Sum(), A0 + B0); // sum after setting a and b const int A = 10; const int B = 2; mCalc.SetA(A); mCalc.SetB(B); QCOMPARE(mCalc.Sum(), A + B); } // generate basic main: no GUI, no events QTEST_APPLESS_MAIN(TestCalculator) // uncomment next line if class declaration is in .cpp (no .h) //#include "TestCalculator.moc" tst_testcalculator.h #ifndef TST_TESTCALCULATOR_H #define TST_TESTCALCULATOR_H #include "calculator.h" #include class TestCalculator: public QObject { Q_OBJECT private slots: // -- setup/cleanup -- void init(); // -- tests -- void testConstructor(); void testSum(); private: const int A0 = 0; const int B0 = 0; private: Calculator mCalc; }; #endif // TST_TESTCALCULATOR_H |