Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Qt и кроссплатформенное программирование С/С++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 21.01.2014, 20:40   #1
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию Пошаговая инструкция. Создание юнит-тестов (модульных тестов) на Qt C++. Разработка через тестирование (TDD - test-driven developm

Привет!

Это пошаговая инструкция для новичков разработчиков на фреймворке Qt.

Принцип 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 на русском, то я рекомендовал бы переключиться на английский. Для этого перейдите в папку: C:\Qt\Qt5.2.0\Tools\QtCreator\share \qtcreator\translations и удалить все файлы в именах которых встречается "_ru"

Для примера разработаем программу, которая решает задачу: http://acmp.ru/index.asp?main=task&id_task=25

- в меню Qt Creator'а выбираем "File" -> "New File or Project..."

- в разделе "Projects" выбираем "Other Project" -> во второй колонке выбираем "Qt Unit Test"

- нажимаем кнопку "Choose"

- в поле "Name" вводим: MyFirstUnit

- нажимаем "Next" -> "Next" -> "Next" -> "Finish"

- нажимаем правой кнопкой мыши по проекту в разделе "Projects" -> нажимаем "Add New...", как показано на рисунке:



- в разделе "Files and Clases" выбираем "C++" -> во втором разделе выбираем "C++ Class"

- нажимаем кнопку "Choose"

- в поле "Class name" вводим: MyClass

- нажимаем кнопку "Next" -> нажимаем кнопку "Finish"

- открываем файл "myclass.h" и меняем его содержимое на следующее:

myclass.h
Код:
#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass
{
public:
    int compare(int first, int second);
};

#endif // MYCLASS_H
- нажимаем правой кнопкой по функции compare() -> выбираем "Refactor" -> выбираем "Add Definition in myclass.cpp", как показано на рисунке:



- открываем файл "myclass.cpp" и меняем его содержимое на следующее:

myclass.cpp
Код:
#include "myclass.h"

int MyClass::compare(int first, int second)
{
    return 0;
}
- открываем файл "tst_myfirstunittest.cpp"

Файле "tst_myfirstunittest.cpp" нам нужно в класс "MyFirstUnitTest" добавить функцию для инициализации данных testCase1_data() (данные - это входные данные и ожидаемый результат)

Ещё нам нужно написать реализацию для функций testCase1_data() и testCase1()

testCase1_data() - инициализирует данные (входные данные и ожидаемый результат)

testCase1() - запускает тесты, то есть производит вызовы тестируемой функции и сравнивает каждый результат вызова с соответствующим ожидаемым результатом

По условию задачи, нужно написать функцию, которая возвращает: -1 - если первый аргумент меньше второе, 1 - если первый аргумент больше второе и 0 - если оба аргумента равны друг другу

Напишем 10 тестов. В данном случае мы могли бы обойтись и меньшем количеством

- меняем содержимое файла "tst_myfirstunittest.cpp" на следующее:

tst_myfirstunittest.cpp
Код:
#include <QString>
#include <QtTest>
#include "myclass.h"

class MyFirstUnitTest : public QObject
{
    Q_OBJECT

public:
    MyFirstUnitTest();

private Q_SLOTS:
    void testCase1_data();
    void testCase1();
};

MyFirstUnitTest::MyFirstUnitTest()
{
}

void MyFirstUnitTest::testCase1_data()
{
    QTest::addColumn<int>("first");
    QTest::addColumn<int>("second");
    QTest::addColumn<int>("expected");

    QTest::newRow("compare01") << 0 << 0 << 0;
    QTest::newRow("compare02") << 5 << 10 << -1;
    QTest::newRow("compare03") << 10 << 5 << 1;
    QTest::newRow("compare04") << -5 << -10 << 1;
    QTest::newRow("compare05") << -10 << -5 << -1;
    QTest::newRow("compare06") << 5 << -10 << 1;
    QTest::newRow("compare07") << -5 << 10 << -1;
    QTest::newRow("compare08") << 2000000000 << 10 << 1;
    QTest::newRow("compare09") << -10 << 2000000000 << -1;
    QTest::newRow("compare10") << 10 << -2000000000 << 1;
}

void MyFirstUnitTest::testCase1()
{
    MyClass mc;

    QFETCH(int, first);
    QFETCH(int, second);
    QFETCH(int, expected);

    int actual = mc.compare(first, second);
    QCOMPARE(actual, expected);
}

QTEST_APPLESS_MAIN(MyFirstUnitTest)

#include "tst_myfirstunittest.moc"
- нажимаем Ctrl+Shift+S, чтобы сохранить всё

Последний раз редактировалось Alex11223; 12.06.2018 в 22:21.
8Observer8 вне форума Ответить с цитированием
Старый 21.01.2014, 20:42   #2
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

- нажимаем Ctrl+R для построения и запуска программы

- в окне "Application Output" мы можем видеть, что удачно завершился только первый тест, так как функция compare() всегда возвращает ноль. Мы передали два нуля и ожидали ноль в первом тесте.



Пишем функционал compare() в файле "myclass.cpp", чтобы она делала, то что мы ожидаем. С помощью тестов мы описали, что мы ожидаем от работы функции compare(). Если мы реализуем неправильно, то тесты нам это покажут.

- меняем содержимое файла "myclass.cpp" на следующее:

myclass.cpp
Код:
#include "myclass.h"

int MyClass::compare(int first, int second)
{
    int result;
    
    if (first > second) {
        result = 1;
    } else if (first < second) {
        result = -1;
    } else {
        result = 0;
    }

    return result;
}
- нажимаем Ctrl+R для построения и запуска программы

Теперь мы видим, что тесты пройдены успешно:



[/B]Примечание:[/B] Чтобы отформатировать код (выровнить), нужно нажать Ctrl+A (чтобы выделить), потом нажать Ctrl+I.

Подробнее о TDD можно почитать здесь: ru.wikipedia.org/wiki/Разработка_через_тестирование
8Observer8 вне форума Ответить с цитированием
Старый 22.01.2014, 10:21   #3
vvmcpp
Форумчанин
 
Аватар для vvmcpp
 
Регистрация: 11.12.2010
Сообщений: 116
По умолчанию

Здравствуйте. Может вопрос не в эту ветку, извините. Раньше я не работал с QT. Установил Qt Creator.
Дохожу до места "введение и размещение проекта", выбор компонентов.





Какие компоненты нужно доустановить или настроить?
QtCreator 3.0
vvmcpp вне форума Ответить с цитированием
Старый 22.01.2014, 10:38   #4
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Привет! Вы хотите, чтобы здесь началась обсуждение другой темы на 10 страниц? Создайте новую тему.
8Observer8 вне форума Ответить с цитированием
Старый 22.01.2014, 11:30   #5
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,618
По умолчанию

Если эта тема для обсуждения инструкции - то я уже написал "не бери тупые примеры". На хабре есть статья гораздо более высокого качества: http://habrahabr.ru/post/146449/ . И пример там похожий. Зачем читать твою статью если есть лучше?

У любого текста должна быть категория читателей. Я вот не пойму, для кого ты пишешь? Если для домохозяек - то нахрена им юнит-тесты. Если для программистов, то к чему такие детали:
Цитата:
- нажимаем Ctrl+Shift+S, чтобы сохранить всё
rrrFer вне форума Ответить с цитированием
Старый 22.01.2014, 12:37   #6
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Я пишу для начинающих и для себя. Мне не хватало и не хватает пошаговых инструкций. Буду углубляться шаг за шагом. Пусть у меня инструкции очень простые. Зато их может легко воспроизвести новичок. А лично для меня они как памятка. Да и есть направление развития. Кроме того, у меня пошаговое мышление и такой подход для меня самый оптимальный. Я сам учусь и от меня возможно будет кому-нибудь польза. Если хотя бы одному человеку будет полезно, то я не зря трудился. А это уже выполнено, потому что одному человеку это уже стало полезно, а именно мне

На пошаговые инструкции меня вдохновило сообщество NetBeans. У них прекрасные инструкции: https://netbeans.org/kb/index_ru.html Вот бы для разработчиков на Qt такое было. Мне нравится это направление (пошаговых инструкций). Я не намерен останавливаться. Вот нравится и всё тут. Это моё хобби. О вкусах не спорят
8Observer8 вне форума Ответить с цитированием
Старый 27.01.2014, 13:34   #7
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

Тестирование методов класса, которые возвращают вещественные числа

Следующий пример отличается от того, что в инструкции тем, что в этом, тестируемых методов два и они возвращают вещественные числа. Тестируем методы "расчёт_прощади" "расчёт_объёма" класса "Сфера".

К сожалению, в Qt нет макросов для сравнения вещественных чисел с заданной точностью, наподобие, к примеру, макроса из CppUnit (который сравнивает ожидаемый результат (expected) и результат работы функции (actual) с заданой точностью дельта (delta)):

Код:
CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta);
Поэтому будем использовать свою функцию, реализацию которой мне подсказали на этом форуме год назад: http://www.qtcentre.org/threads/5290...ers?highlight=

Код:
static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
}
Все файлы проекта TestShere:

sphere.h
Код:
#ifndef SPHERE_H
#define SPHERE_H

class Sphere
{
public:
    double calcArea(double radius);
    double calcVolume(double radius);
};

#endif // SPHERE_H
sphere.cpp
Код:
#include "sphere.h"
#include <cmath>

double Sphere::calcArea(double radius) {
    double area = 4.0 * M_PI * pow(radius, 2.0);
    return area;
}

double Sphere::calcVolume(double radius) {
    double volume = (4.0/3.0) * M_PI * pow(radius, 3.0);
    return volume;
}
tst_testspheretest.cpp
Код:
#include <QString>
#include <QtTest>
#include "sphere.h"
#include <QDebug>

class TestSphereTest : public QObject
{
    Q_OBJECT

public:
    TestSphereTest();

    static inline bool qFuzzyCompare(double p1, double p2, double delta)
    {
        return (qAbs(p1 - p2) <= delta * qMin(qAbs(p1), qAbs(p2)));
    }

private Q_SLOTS:
    void testCalcArea_data();
    void testCalcArea();

    void testCalcVolume_data();
    void testCalcVolume();
};

TestSphereTest::TestSphereTest()
{
}

void TestSphereTest::testCalcArea_data()
{
    QTest::addColumn<double>("radius");
    QTest::addColumn<double>("expected");

    QTest::newRow("calcArea01") << 6.0 << 452.3893;
    QTest::newRow("calcArea02") << 7.0 << 615.7527;
    QTest::newRow("calcArea03") << 8.0 << 804.2477;
}

void TestSphereTest::testCalcArea()
{
    Sphere s;

    QFETCH(double, radius);
    QFETCH(double, expected);

    double actual = s.calcArea(radius);
    double delta = 0.0001;
    bool result = qFuzzyCompare(actual, expected, delta);
    QVERIFY2(result, "Failure");
}

void TestSphereTest::testCalcVolume_data()
{
    QTest::addColumn<double>("radius");
    QTest::addColumn<double>("expected");

    QTest::newRow("calcVolume01") << 6.0 << 904.7787;
    QTest::newRow("calcVolume02") << 7.0 << 1436.7550;
    QTest::newRow("calcVolume03") << 8.0 << 2144.6606;
}

void TestSphereTest::testCalcVolume()
{
    Sphere s;

    QFETCH(double, radius);
    QFETCH(double, expected);

    double actual = s.calcVolume(radius);
    double delta = 0.0001;
    bool result = qFuzzyCompare(actual, expected, delta);
    QVERIFY2(result, "Failure");
}

QTEST_APPLESS_MAIN(TestSphereTest)

#include "tst_testspheretest.moc"
TestShere.pro
Код:
QT       += testlib

QT       -= gui

TARGET = tst_testspheretest
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += tst_testspheretest.cpp \
    sphere.cpp
DEFINES += SRCDIR=\\\"$$PWD/\\\"

HEADERS += \
    sphere.h

Последний раз редактировалось 8Observer8; 28.01.2014 в 10:21.
8Observer8 вне форума Ответить с цитированием
Старый 27.01.2014, 14:20   #8
ROD
Linux C++ Qt ARM
Старожил
 
Аватар для ROD
 
Регистрация: 30.11.2008
Сообщений: 3,030
По умолчанию

Цитата:
- если у Вас Qt Creator на русском, то я рекомендовал бы переключиться на английский. Для этого перейдите в папку: C:\Qt\Qt5.2.0\Tools\QtCreator\share \qtcreator\translations и удалить все файлы в именах которых встречается "_ru"
А я, как дурак, в настройках язык меняю. "Инструменты" -> "Параметры" -> "Среда" -> Вкладка "Основное" -> В пункте язык вместо "Системный" выбрать нужный вам. (См. Рисунок)
Изображения
Тип файла: jpg qt creator settings.jpg (24.8 Кб, 133 просмотров)
Дилетант широкого профиля.

"Слова ничего не стоят - покажите мне код!" © Линус Торвальдс

Последний раз редактировалось ROD; 27.01.2014 в 14:35.
ROD вне форума Ответить с цитированием
Старый 27.01.2014, 14:57   #9
8Observer8
Старожил
 
Аватар для 8Observer8
 
Регистрация: 02.01.2011
Сообщений: 3,322
По умолчанию

ROD, спасибо! Было бы здорово, если бы модератор там отредактировал так:

- если у Вас Qt Creator на русском, то я рекомендовал бы переключиться на английский. Для этого выбираем в меню "Инструменты" -> "Параметры..."

- в окне "Параметры" слева выбираем "Среда" -> открываем вкладку "Основные" -> выбираем в выпадающем списке "English" -> нажимаем кнопку "OK" -> перезапускаем Qt Creator
8Observer8 вне форума Ответить с цитированием
Старый 27.01.2014, 16:30   #10
rrrFer
Санитар
Старожил
 
Аватар для rrrFer
 
Регистрация: 04.10.2008
Сообщений: 2,618
По умолчанию

Цитата:
К сожалению, в Qt нет макросов для сравнения вещественных чисел с заданной точностью, наподобие, к примеру, макроса из CppUnit (который сравнивает ожидаемый результат (expected) и результат работы функции (actual) с заданой точностью дельта (delta)):

...

Поэтому будем использовать свою функцию, реализацию которой мне подсказали на этом форуме год назад:
Да ты что? - на том форуме тебя жестко обманули и дали кусок кода из qglobal.h (а выдали за свой, раз ты не вкурсе что это стандартная функция и пишешь его в каждом проекте).
Но вместо того, чтобы спрашивать на форумах, можно поискать в ассистанте. А перед тем как утверждать, что чего-то нет, можно опять же поискать в ассистанте. Я не помню время, когда в кьют не было qFuzzyCompare.

Код:
Q_DECL_CONSTEXPR static inline bool qFuzzyCompare(double p1, double p2) Q_REQUIRED_RESULT;
Q_DECL_CONSTEXPR static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) * 1000000000000. <= qMin(qAbs(p1), qAbs(p2)));
}

Q_DECL_CONSTEXPR static inline bool qFuzzyCompare(float p1, float p2) Q_REQUIRED_RESULT;
Q_DECL_CONSTEXPR static inline bool qFuzzyCompare(float p1, float p2)
{
    return (qAbs(p1 - p2) * 100000.f <= qMin(qAbs(p1), qAbs(p2)));
}
Скопипащено дословно.
rrrFer вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Конструктор тестов + онлайн тестирование sereniaaa Фриланс 6 17.01.2014 23:10
TDD (Test-driven development) для C# (csUnit) 8Observer8 C# (си шарп) 12 22.11.2012 13:59
статья - Test-Driven Development для Java(TDD, разработка через тестирование) на практике. Pblog Обсуждение статей 0 16.06.2011 23:10