1. Темы лекции: Unit-тестирование в Qt.
Практическое задание: Unit-тестирование в Qt.
Тренер: Игорь Шкулипа, к.т.н.
С++ Библиотеки STL и Qt. Занятие 10
2. http://www.slideshare.net/IgorShkulipa 2
Модульное тестирование
Unit testing (юнит тестирование или модульное тестирование) —
заключается в изолированной проверке каждого отдельного элемента
путем запуска тестов в искусственной среде.
Unit (Элемент) — наименьший компонент, который можно
скомпилировать.
Драйверы — модули тестов, которые запускают тестируемый элемент.
Заглушки — заменяют недостающие компоненты, которые вызываются
элементом и выполняют следующие действия:
• возвращаются к элементу, не выполняя никаких других действий;
• отображают трассировочное сообщение и иногда предлагают
тестировщику продолжить тестирование;
• возвращают постоянное значение или предлагают тестеру самому
ввести возвращаемое значение;
• осуществляют упрощенную реализацию недостающей
компоненты;
• имитируют исключительные или аварийные условия.
3. http://www.slideshare.net/IgorShkulipa 3
Подходы к unit-тестированию
White-box testing. Для конструирования тестов используются
внутренняя структура кода и управляющая логика. При этом существует
вероятность, что код будет проверяться так, как он был написан, а это не
гарантирует корректность логики.
Black-box testing. Для конструирования тестов используются требования
и спецификации ПО.
Недостатки:
• таким способом невозможно найти взаимоуничтожающихся
ошибок,
• некоторые ошибки возникают достаточно редко (ошибки работы с
памятью) и потому их трудно найти и воспроизвести
4. http://www.slideshare.net/IgorShkulipa 4
Стратегия модульного тестирования
Модульное тестирование является одной из ключевых практик
методологии экстремального программирования:
• Написание тестов помогает войти в рабочий ритм
• Придает уверенность в работоспособности кода.
• Дает запас прочности при дальнейшей интеграции или изменениях
кода.
Модульное тестирование оправдано, если оно:
• Снижает время на отладку
• Дает возможность поиска ошибок с меньшими затратами, нежели
при других подходах
• Дает возможность дешевого поиска ошибок при изменениях кода в
дальнейшем
5. http://www.slideshare.net/IgorShkulipa 5
Цель модульного тестирования
Получение работоспособного кода с наименьшими затратами. И его применение
оправдано тогда и только тогда, когда оно дает больший эффект, нежели другие
методы.
Отсюда следует несколько выводов:
• Нет смысла писать тесты на весь код. Некоторые ошибки проще найти
на более поздних стадиях. Так, например, для ООП данное правило может
звучать так: нет смысла писать тесты на класс, который используется
только одним классом. Эффективней написать тесты на вызывающий класс
и создать тесты тестирующие все участки кода.
• Писать тесты для кода потенциально подверженного изменениям
более выгодно, чем для кода, изменение которого не предполагается.
Сложная логика меняется чаще, чем простая. Следовательно, в первую
очередь имеет смысл писать модульные тесты на сложную логику. А на
простую логику писать позднее или вообще тестировать другими методами.
• Для того чтобы как можно реже изменять тесты следует хорошо
планировать интерфейсы. То же самое можно сказать и применительно к
написанию исходного кода. Действительно, создание хорошей архитектуры
часто определяет дальнейший ход проекта. И есть оптимум, на каком этапе
архитектура «достаточно хороша».
6. http://www.slideshare.net/IgorShkulipa 6
Планирование тестов
• Код с не оттестированными участками не может быть опубликован
• Тесты должны базироваться на спецификации
• На каждое требование должен быть, как минимум, один тест. Неважно,
ручной или автоматический
• Простой тест нужен т.к. несмотря на малую вероятность нахождения
ошибки, цена пропущенной ошибки чрезмерно высока
• Наиболее эффективный способ создания тестового набора —
совместное использование методов черного и белого ящиков.
7. http://www.slideshare.net/IgorShkulipa 7
TDD
TDD (Test-Driven Development) - это методика разработки, позволяющая
оптимизировать использование модульных тестов. Стоит подчеркнуть,
что речь идет именно об оптимальном, а не максимальном
применении. Задача, которую преследует TDD, - достижение баланса
между усилиями и результатом.
С практической точки зрения, основой TDD является цикл
«red/green/refactor»:
• в первой фазе программист пишет тест,
• во второй - код, необходимый для того, чтобы тест работал,
• в третьей, при необходимости, производится рефакторинг.
В соответствии с принципом "Test First", следует писать только такой
код, который абсолютно необходим, чтобы тесты выполнялись
успешно.
8. http://www.slideshare.net/IgorShkulipa 8
Возможности Unit-тестирования в Qt
В Qt за юнит-тестирование отвечает модуль QTestLib (testlib). Он
предоставляет набор макросов для тестирования.
Есть несколько методов проведения тестов:
• Создать тестовый проект в дочерней директории тестируемого
проекта и тестировать в нем
• Тестировать методом qExec(…) в основном проекте
18. http://www.slideshare.net/IgorShkulipa 18
Макросы
QBENCHMARK - используется для измерения производительности кода в тесте.
QBENCHMARK_ONCE - используется для измерения производительности кода в тесте, но
запускается только один раз.
QCOMPARE(actual, expected) - сравнивает фактическое значение с ожидаемым
значению с помощью оператора сравнения.
QEXPECT_FAIL(dataIndex, comment, mode) - отмечает следующий QCOMPARE() или
QVERIFY() в качестве ожидаемой ошибки. Вместо того печати ошибки в лог, будет
сообщение, что ожидается ошибка.
QFAIL(message) - этот макрос может использоваться, чтобы вызвать ошибки теста
QFETCH(type, name) - создает локальную переменную в стеке с именем и типом
QFINDTESTDATA(filename) - возвращает QString для TestData указанного по имени
файла, или пустой QString если файл не может быть найден.
QSKIP(description) – вызванный из тестового метода, макрос останавливает
выполнение теста без добавления ошибки в лог.
QTEST(actual, testElement) - сравнивает фактическое значение с элементом
testElement из данных теста.
QTEST_APPLESS_MAIN(TestClass) - реализует функцию main(), которая выполняет все
тесты в TestClass.
QTEST_GUILESS_MAIN(TestClass) - реализует функцию main(), которая создает объект
QCoreApplication и TestClass, и выполняет все тесты в порядке в котором они были
определены.
19. http://www.slideshare.net/IgorShkulipa 19
Макросы
QTEST_MAIN(TestClass) - реализует функцию main(), которая создает объект
приложения и TestClass, и выполняет все тесты в порядке в котором они были
определены.
QTRY_COMPARE(actual, expected) - выполняет сравнение фактического и ожидаемого
значений путем вызова QTRY_COMPARE_WITH_TIMEOUT () с таймаутом пять секунд.
QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout) - похож на QCOMPARE(),
но выполняет сравнение фактического и ожидаемого значений неоднократно, пока
либо два значения не равны или превышен тайм-аут.
QTRY_VERIFY(condition) - проверяет состояние, вызывая
QTRY_VERIFY_WITH_TIMEOUT () с таймаутом пять секунд.
QTRY_VERIFY_WITH_TIMEOUT(condition, timeout) - похож на QVERIFY(), но проверяет
условие неоднократно, пока либо оно выполнится или превышен тайм-аут.
QVERIFY2(condition, message) - ведет себя так же, как QVERIFY(), за исключением
того, он выводит сообщение, когда условие ложно.
QVERIFY(condition) - проверяет, является ли условие истинным или нет. Если
истинно, то выполнение продолжается. Если нет, то сбой записывается в логи тест
дальше не выполняется.
QWARN(message) – добавляет предупреждение в лог.