Внимание! Эта тема перетекла, в следующую тему, в шапку которой я буду добавлять изменения/добавления. Вам достаточно будет подписаться на неё. О всех измененияx/добавленияx я буду извещать: http://www.prog.org.ru/topic_26944_0.htmlПошаговая инструкция. Разработка ПО на Qt C++ по методике TDD (test-driven development)Инструментарий:- Qt 5.2.0 for Windows 32-bit (MinGW 4.8, OpenGL, 689 MB)
- Компилятор: MinGW 4.8
- Операционная система: Windows 7
Введение:Принцип TDD (test-driven development - разработка ПО через тестирование) - сначала пишем тесты для пустого модуля (модуль - это функция или класс), а потом реализацию для этого модуля.
Такой способ разработки позволяет:
- описывать функциональность до написания самой функции
- демонстрировать, что функциональность реализована, как это и требовалось (заказчику, пользователю и\или самому себе)
- проводить рефакторинг (усовершенствовать код с целью оптимизации и/или для улучшения читабельности) без опасения нарушения функциональности
Инструкция:- скачиваем и устанавливаем "Qt 5.2.0 for Windows 32-bit (MinGW 4.8, OpenGL, 689 MB)"
http://qt-project.org/downloads- запускаем Qt Creator
- если у Вас Qt Creator на русском, то я рекомендовал бы переключиться на английский. Для этого выбираем в меню "Инструменты" -> "Параметры..."
- в окне "Параметры" слева выбираем "Среда" -> открываем вкладку "Основные" -> выбираем в выпадающем списке "English" -> нажимаем кнопку "OK" -> перезапускаем Qt Creator
Допустим у нас задание: разработать класс с названием MyDate, в котором будет один метод с названием nameOfMonth(). Этот метод принимает число от 1 до 12 и возвращает название месяца. Если входной параметр метода не входит в диапазан [1, 12], то метод вернёт строку "Error: incorrect input data"
- в меню Qt Creator'а выбираем "File" -> "New File or Project..."
- в разделе "Projects" выбираем "Other Project" -> во второй колонке выбираем "Qt Unit Test"
- нажимаем кнопку "Choose"
- в поле "Name" вводим: TestMyDate
- нажимаем "Next" -> "Next" -> "Next" -> "Finish"
- нажимаем правой кнопкой мыши по проекту в разделе "Projects" -> нажимаем "Add New...", как показано на рисунке:
![](http://i7.pixs.ru/storage/9/9/2/001png_2733611_10594992.png)
- в разделе "Files and Clases" выбираем "C++" -> во втором разделе выбираем "C++ Class"
- нажимаем кнопку "Choose"
- в поле "Class name" вводим: MyDate
- нажимаем кнопку "Next" -> нажимаем кнопку "Finish"
- открываем файл "mydate.h" и меняем его содержимое на следующее:
mydate.h
#ifndef MYDATE_H
#define MYDATE_H
#include <QString>
class MyDate
{
public:
QString nameOfMonth(int n);
};
#endif // MYDATE_H
- нажимаем правой кнопкой по функции nameOfMonth() -> выбираем "Refactor" -> выбираем "Add Definition in myclass.cpp", как показано на рисунке:
![](http://i7.pixs.ru/storage/3/6/6/002png_9685229_10596366.png)
- меняем содержимое файла "mydate.cpp" на следующее:
mydate.cpp
#include "mydate.h"
QString MyDate::nameOfMonth(int n)
{
return "";
}
- открываем файл "tst_testmydatetest.cpp" и меняем его содержимое на следующее:
tst_testmydatetest.cpp
#include <QString>
#include <QtTest>
#include "mydate.h"
class TestMyDateTest : public QObject
{
Q_OBJECT
public:
TestMyDateTest();
private Q_SLOTS:
void testCase1_data();
void testCase1();
};
TestMyDateTest::TestMyDateTest()
{
}
void TestMyDateTest::testCase1_data()
{
QTest::addColumn<int>("n");
QTest::addColumn<QString>("expected");
QTest::newRow("nameOfMonth01") << 1 << "January";
QTest::newRow("nameOfMonth02") << 2 << "February";
QTest::newRow("nameOfMonth03") << 3 << "March";
QTest::newRow("nameOfMonth04") << 4 << "April";
QTest::newRow("nameOfMonth05") << 5 << "May";
QTest::newRow("nameOfMonth06") << 6 << "June";
QTest::newRow("nameOfMonth07") << 7 << "July";
QTest::newRow("nameOfMonth08") << 8 << "August";
QTest::newRow("nameOfMonth09") << 9 << "September";
QTest::newRow("nameOfMonth10") << 10 << "October";
QTest::newRow("nameOfMonthe11") << 11 << "November";
QTest::newRow("nameOfMonth12") << 12 << "December";
QTest::newRow("nameOfMonthe13") << 0 << "Error: incorrect input data";
QTest::newRow("nameOfMonth14") << -1 << "Error: incorrect input data";
QTest::newRow("nameOfMonth15") << 13 << "Error: incorrect input data";
}
void TestMyDateTest::testCase1()
{
MyDate md;
QFETCH(int, n);
QFETCH(QString, expected);
QString actual = md.nameOfMonth(n);
QCOMPARE(actual, expected);
}
QTEST_APPLESS_MAIN(TestMyDateTest)
#include "tst_testmydatetest.moc"
И так, рассмотрим кратко, что мы изменили в файле "tst_testmydatetest.cpp":
- добавили метод testCase1_data() в класс "TestMyDateTest" для инициализации данных
Данные инициализируются так:
QTest::newRow("nameOfMonth01") << 1 << "January";
QTest::newRow("nameOfMonth02") << 2 << "February";
QTest::newRow("nameOfMonth03") << 3 << "March";
Первыми идут параметры тестируемого метода, а последним - ожидаемый результат.
- добавили функцию сравнения ожидаемого результата и результата, который возвращает тестируемый метод:
void TestMyDateTest::testCase1()
{
MyDate md;
QFETCH(int, n);
QFETCH(QString, expected);
QString actual = md.nameOfMonth(n);
QCOMPARE(actual, expected);
}
- нажимаем Ctrl+R для построения и запуска программы
- в окне "Application Output" мы можем видеть, что не один из вызовов тестируемого метода не завершился положительно:
![](http://i7.pixs.ru/storage/0/4/0/003png_7433496_10596040.png)
- пишем функционал nameOfMonth(), для этого меняем содержимое файла "mydate.cpp" на следующее:
#include "mydate.h"
QString MyDate::nameOfMonth(int n)
{
QString month;
switch (n) {
case 1:
month = "January";
break;
case 2:
month = "February";
break;
case 3:
month = "March";
break;
case 4:
month = "April";
break;
case 5:
month = "May";
break;
case 6:
month = "June";
break;
case 7:
month = "July";
break;
case 8:
month = "August";
break;
case 9:
month = "September";
break;
case 10:
month = "October";
break;
case 11:
month = "November";
break;
case 12:
month = "December";
break;
default:
month = "Error: incorrect input data";
break;
}
return month;
}
- нажимаем Ctrl+R для построения и запуска программы
Теперь мы видим, что тесты пройдены успешно:
![](http://i7.pixs.ru/storage/1/8/2/004png_4315808_10596182.png)
P.S. Подробнее о TDD можно почитать здесь:
ru.wikipedia.org/wiki/Разработка_через_тестированиеДобавил 07.04.2014 12:41Пример разработки ПО через тестированиеЗадание на разработку.. Написать класс с именем FiveAndFive. В этом классе должен быть метод fiveAndFive(), который возвращает квадрат своего аргумента.
Соглашения вызова метода:1) Метод работает с аргументом из диапазона: [5, 4*10^5]. Если нарушено это соглашение, то метод выбрасывает исключение.
2) Метод работает с аргументом кратным пяти. Если нарушено это соглашение, то метод выбрасывает исключение.
- создаём консольное приложение "Qt Console Application"
Имя папки: FiveAndFiveProject
Имя проекта: FiveAndFive
- создаём проект "Qt Unit Test" в той же папке: FiveAndFiveProject
Имя проекта: FiveAndFiveTests
Имя класса с тестами: FiveAndFiveTests
Имя исходного файла с тестами: tst_FiveAndFiveTests.cpp
- в проекте FiveAndFive создаём класс с тем же именем: FiveAndFive, а так же создаём заглушку для метода fiveAndFive(). Так же добавляем классы исключений (код проекта см. ниже)
Весь код проекта FiveAndFive: https://github.com/8Observer8/FiveAndFiveВесь код проекта FiveAndFiveTests: https://github.com/8Observer8/FiveAndFiveTestsДобавил новую информацию 4/24/2014 8:45 AMДля пользовательских типов надо, чтобы фреймворк поддерживал Mock-объекты:
http://ru.wikipedia.org/wiki/Mock-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82TDD на Западе очень популярен. Microsoft даже включил его в Visual Studio, начиная с версии 2012:
![](http://i6.pixs.ru/storage/1/2/2/188png_8729542_11811122.png)
Причём они реализовали TDD так, что можно создавать классы и методы (загрушки) прямо из тестов. Таким образом, сначала пишем тесты, а потом открываем файл с классами и начинаем писать реализацию для методов (часто запуская тесты), пока вся полоса не будет зелёной. Вот здесь на примере можно понять философию методологии "Разработка через тестирование":
http://msdn.microsoft.com/en-us/library/hh212233.aspxК сожалению, QTest в Qt не поддерживает Mock-объекты, поэтому придётся изучать фреймворки "Google Mock" и\или "Boost Mock". На данном этапе, QTest вполне устраивает. Смог обойти проблему, что QTest не поддерживает тестирование исключений и сравнение вещественных чисел с дельтой. Для демонстрации есть пример:
- проект:
https://github.com/8Observer8/FiveAndFive- проект "Qt Unit Test":
https://github.com/8Observer8/FiveAndFiveTestsВ CppUnit это есть:
Сравнение вещественных чисел с дельтой:
C++ (Qt)
CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( message, expected, actual, delta );
Тестирование на выброс исключения:
C++ (Qt)
CPPUNIT_ASSERT_THROW_MESSAGE( message, expression, ExceptionType );
Пример из фреймворка CppUnit:
C++ (Qt)
std::vector<int> v;
CPPUNIT_ASSERT_THROW_MESSAGE( "- std::vector<int> v;", v.at( 50 ), std::out_of_range );
Подробно о TDD написано в книгах:
- Мартин Фаулер - Рефакторинг. Улучшение существующего кода
- Кент Бек. Экстремальное программирование. Разработка через тестирование
- Р. Мартин. Быстрая разработка программного обеспечения. Принципы, практика, примеры (в примерах использованы языки C++ и Java)
- Мартин Р.С., Мартин М. - Принципы, паттерны и методики гибкой разработки на языке C# - 2011