Russian Qt Forum

Qt => Общие вопросы => Тема начата: 8Observer8 от Апрель 05, 2014, 13:08



Название: [Решено] Прикручивание фреймворка CppUnit к Qt
Отправлено: 8Observer8 от Апрель 05, 2014, 13:08
Добавил 4/30/2014 07:40 PM
Фреймворк CppUnit уже давно не поддерживается и не развивается. К тому же, у него отсутствует возможность создавать Mock-объекты. Здесь инструкция по самому продвинутому TDD-фреймворку на C++ GTest - http://www.prog.org.ru/topic_26944_0.html

Привет!

Мне надо подсоединить к проекту внешние заголовочные файлы в Qt Creator из этой папки: C:/cygwin64/usr/include/cppunit/ Подскажите, пожалуйста, как это правильно сделать?

Я создал пустой проект.

В файле .pro я пишу:
Код
C++ (Qt)
HEADERS += C:/cygwin64/usr/include/
 

В файле main.cpp
Код
C++ (Qt)
#include <cppunit/TestRunner.h>
 
int main() {
   return 0;
}
 

Но компилятор ругается:
Цитировать
main.cpp:1: error: cppunit/TestRunner.h: No such file or directory
 #include <cppunit/TestRunner.h>
                                            ^

Заранее спасибо за ответ!


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: VPS от Апрель 05, 2014, 13:46
Вместо "HEADERS" используйте "INCLUDEPATH".


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 05, 2014, 14:21
Спасибо! Но теперь я не могу понять, почему безобидное включение файла вызвало столько ошибок?

(http://i7.pixs.ru/storage/7/5/6/161png_8378221_11557756.png)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 05, 2014, 14:29
Потому что вы вместо разбора проблемы ищите решение :)

Немного информации. Один заголовочный файл подключить можно прямым инклудом
Код:
#include "c:/cygwin/blabla/tuktuk.h"
Но если в нем содержатся инклуды других файлов, то выпадет куча ошибок, как и у вас, ибо компилятор не сможет найти нужные вам файлы.

Включил в IncludePath папку вы решите эту проблему, но столкнётесь с тем, что некоторые файлы у вас будут дублироваться и компилятор будет в недоумении - какой же файл брать.

Я не могу вам точно сказать что у вас там происходит (не знаток mingw), но на мой взгляд у вас дублирование инклудов ;)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 05, 2014, 14:39
Спасибо большое! Скорее всего, Вы правы и возможно здесь конфликт cygwin64 и mingw. Тот заголовочный файл, который я подключаю, идёт в комплекте со набором инструментария cygwin64.

Как начать разбираться в этой проблеме? Я всего лишь хочу подключить заголовочный файл: C:/cygwin64/usr/include/cppunit/TestRunner.h и чтобы у меня сработал Build. Я вообще не понимаю, что происходит. Помогите, пожалуйста, понять проблему.

Это удивительно, что в инструментарии cygwin64 в модуле CppUnit есть заголовочный файл для Qt:  QtTestRunner.h И он нормально подключается (срабатывает построение):
Код
C++ (Qt)
SOURCES += \
   main.cpp
INCLUDEPATH += "C:/cygwin64/usr/include/"
 

main.cpp
Код
C++ (Qt)
#include <cppunit/ui/qt/QtTestRunner.h>
 
int main() {
   return 0;
}
 

(http://i.pixs.ru/storage/4/3/3/162png_3293966_11558433.png)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 05, 2014, 16:37
Подключить файл и использовать класс описанный в файле - неравнозначные вещи :)

Если вам нужен 1 - его и подключайте :D


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 05, 2014, 17:08
Да там много файлов. Задумка была в переносе в Qt кода для тестирования (реализацию поддержки модуля CppUnit) из NetBeans 7.4. Для начала, вот этого файла:

newtestrunner.cpp
Код
C++ (Qt)
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TestRunner.h>
 
int main() {
   // Create the event manager and test controller
   CPPUNIT_NS::TestResult controller;
 
   // Add a listener that colllects test result
   CPPUNIT_NS::TestResultCollector result;
   controller.addListener(&result);
 
   // Add a listener that print dots as test run.
   CPPUNIT_NS::BriefTestProgressListener progress;
   controller.addListener(&progress);
 
   // Add the top suite to the test runner
   CPPUNIT_NS::TestRunner runner;
   runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
   runner.run(controller);
 
   // Print test in a compiler compatible format.
   CPPUNIT_NS::CompilerOutputter outputter(&result, CPPUNIT_NS::stdCOut());
   outputter.write();
 
   return result.wasSuccessful() ? 0 : 1;
}
 

Дело в том, что QTest не поддерживает сравнения вещественных actual, expected и с погрешностью delta, как это сделано в CppUnit:
Код
C++ (Qt)
CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta);
 

Ещё QTest не умеет отлавливать исключения, а CppUnit умеет:
Код
C++ (Qt)
std::vector<int> v;
CPPUNIT_ASSERT_THROW( v.at( 50 ), std::out_of_range );
 

Так что придёться использовать свои костыли для QTest, а именно, выше названные сравнение вещественных чисел и отлавливание исключений.


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 05, 2014, 18:37
Вот ты б вместо попытки тестировать простое приложение, сделал бы одно сложное :D И сразу бы понял, что этот путь ведёт не к тупику, нет. Он ведёт к 90% тестов, 10% разработки :)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 05, 2014, 19:20
Тесты писать - это просто. Я имею ввиду функции, которые принимают и возвращают простые типы, строки. Они позволяют описать функцию в виде тестов, учесть все тонкости, граничные значения, возвращаемые ошибки (исключения). Если есть тесты, то писать саму функцию более безопасно, как и рефакторить её позже. Можно по тестам понять, как работать с функцией, если прошло время. И заказчику лучше отдавать протестированные классы вместе с тестами. И писать их - это совсем не в напряг. Я не пишу 100 тестов, а где-то в районе 10. На данном этапе развития, мне этого хватает.

Другое дело писать тесты для сложных приложений, которые работают с базами данных, сетями. В таких случаях надо, чтобы фреймворк поддерживал Mock-объекты. К сожалению, QTest и CppUnit их не поддерживают. Для этого надо интегрировать в Qt такие фреймворки, как например: Google Mock или Boost Mock. Но пока остановлюсь на минимуме, а именно на QTest и CppUnit.


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 05, 2014, 19:27
Бесполезно переубеждать :)
За то время, пока вы изучаете тесты и ООП, я в своё время изучил С++ и написал довольно сложную программу :) Тем самым положив начало своей карьере.
По моему разумению программист в 1 очередь должен уметь программировать. Описывать алгоритмы и по ним программировать. Всё остальное вторично, ибо служит либо для оптимизации или для проверки. :) А вы без первого второе изучаете :) А смысл?


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Old от Апрель 05, 2014, 19:42
За то время, пока вы изучаете тесты и ООП
Да не нужно никакого времени для изучения тестов. И на их написание тоже много времени не надо. :)
Вы автор кода, вы знаете, что от него хотите получить, это описываете в тестах. Потратить 10 минут на класс, что бы в любой момент знать, что он рабочий, не очень большая цена.


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 05, 2014, 19:45
Тестирование я уже освоил. Сначала тесты - потом содержимое функции. Мне это сильно помогает. Я чувствую себя уверенее. Надеюсь, что этот подход войдёт в мой несгораемый запас и будет привычкой. Как и привычка писать свои классы исключений, чтобы обрабатывать ошибки и писать тесты для проверки исключений. На этом этапе я Mock-объекты пока изучать не буду (ну может если свободное время появится).

Используя тесты и исключения, теперь я могу писать, писать и писать всё более сложные программы, описывать алгоритмы, по ним писать программы и т.д.

А тесты уже не изучаю, а просто ими пользуюсь. Всегда. Даже для самых простых методов :) Даже для гетеров и сетеров :) Это увлекательно, не могу от этого отказаться, как не переубеждайте :)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: 8Observer8 от Апрель 22, 2014, 10:36
Вот ты б вместо попытки тестировать простое приложение, сделал бы одно сложное :D И сразу бы понял, что этот путь ведёт не к тупику, нет. Он ведёт к 90% тестов, 10% разработки :)

TDD наоборот призвана экономить время. Я понял фишку методологии "Разработки через тестирования". Она почти не имеет ничего общего с модульным тестирование. Модульным тестированием занимается специальный отдел (не программисты). Они тестируют уже готовые модули. На модульное тестирование уходит очень много времени, так как надо написать очень большой набор тестов.

TDD же работает так:
- Сначала пишем заглушку для метода класса
- Пишем несколько тестов по принципу: входные значения метода, ожидаемый результат, вызов метода, сравнение выходного значения метода и ожидаемого результата. На это уходит пара десятков секунд (при условии, что человек это уже делал не раз)
- Запускаем тестирование и убеждаемся, что ни один тест не выполнился (метод же пока без реализации). Как говорят поклонники методологии TDD - полоса на 100% красная ( скорее всего, они так говорят :) )
- Возвращаемся к методу и пишем его реализацию
- Запускаем тестирование и убеждаемся, что полоса полностью зелёная
- Если нам требуется ещё функциональность от нашего метода, то мы пишем ещё несколько тестов, запускаем тестирование, убеждаемся, что полоса не на 100% зелёная, дописываем реализацию метода, чтобы удовлетворить новые тесты, запускаем тестирование, убеждаемся, что полоса на 100% зелёная
- Теперь мы можем при желании отрефакторить код внутри метода. После внесённых изменений опять запускаем тесты и убеждаемся, что мы ничего не испортили
- Это удобно, когда кто-то из команды неправильно понял внутреннюю работу метода, внес изменения (например для оптимизации или\и улучшения читабельности) и нечаянно испортил функциональность. Этот же человек может запустить тесты и убедиться, что он испортил
- Это так же удобно, если от метода требуется новая функциональность, или надо добавить какую-то защиту (чтобы метод ожидаемо реагировал на некорректные входные данные) Мы можем дописать новые тесты, запустить тесты, дописать функциональность в метод, запустить тесты
- А то что к концу разработки класса(ов) создаётся небольшой набор тестов, который демонстрирует (хотя бы минимальную) работоспособность системы - я так понимаю, это приятный бонус.
- Разработчик изначально проектирует методы, которые "можно будет тестировать". Это облегчает работу отделу тестирования


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 22, 2014, 11:03
Вопрос - как вы будете искать неполадку в программе, если все тесты зелёные? Проблема TDD в том, что вы пишете реализацию и проверяете её на 1 массиве данных. А если она на числе 102 даёт ошибку? Или же как проверить работоспособность метода, который использует метод?

PS последнее из моей практики - на числах от 200 до 255 выдаёт неверное значение. Вроде бы просто, но тогда тесты у вас должны учитывать все варианты ошибок )


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: OKTA от Апрель 22, 2014, 12:01
Что-то я до сих пор не могу догнать философию этого тестирования  :-\
Если, к примеру, метод возвращает и принимает int, то что он еще может вернуть и принять, кроме int? А если могут быть какие-то конкретные значения 1, 10, 28, 30 и т.д., то это же надо проверять в самом методе  ??? Зачем тогда тут тесты?  ???


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 22, 2014, 13:15
Метод направлен на правильное вычисление значения :D
Т.е. по сути подбирается массив проверочных значений и результат функции сравнивается с ожидаемым результатом в тесте. Вот только это работает на простейших функциях :)


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: OKTA от Апрель 22, 2014, 13:24
Вот шайтан-метод  ;D


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: Bepec от Апрель 22, 2014, 13:53
А самое в нём интересное, что покрытие теста минимальное. Т.е. массив редко бывает больше 50-100 значений и ответов.
Если по серьёзному подходить, то необходима проверка всех вариантов аргументов в зависимости от типа. Но руками такое вбивать в тест можно, но бессмысленно.
Идём дальше.
Возьмём тогда класс для проверки который будет генерить аргументы, ожидаемые ответы и тестировать. Вроде решили проблему :)
Ан нет, ведь никто не может сказать что этот класс считает верно, следовательно и его проверить надо :D

PS замкнутый круг ^.^


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы?
Отправлено: OKTA от Апрель 22, 2014, 14:09
Девочки-тестировщики незаменимы  ;D


Название: Re: Как правильно подсоединить к проекту внешние загаловочные файлы? TDD в Qt
Отправлено: 8Observer8 от Апрель 23, 2014, 07:33
Для пользовательских типов надо, чтобы фреймворк поддерживал Mock-объекты: http://ru.wikipedia.org/wiki/Mock-%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82

TDD на Западе очень популярен. 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


Название: Re: [Решено] Прикручивание фреймворка CppUnit к Qt
Отправлено: 8Observer8 от Апрель 30, 2014, 06:44
Фреймворк CppUnit уже давно не поддерживается и не развивается. К тому же, у него отсутствует возможность создавать Mock-объекты. Здесь инструкция по самому продвинутому TDD-фреймворку на C++ GTest - http://www.prog.org.ru/topic_26944_0.html


Название: Re: [Решено] Прикручивание фреймворка CppUnit к Qt
Отправлено: OKTA от Апрель 30, 2014, 09:50
А почему не поддерживается и не развивается? всего 5 месяцев назад последняя версия вышла.


Название: Re: [Решено] Прикручивание фреймворка CppUnit к Qt
Отправлено: 8Observer8 от Апрель 30, 2014, 11:01
Да, я видел в википедии. Его не развивают, а просто что-то там отрефакторили. Самое очевидное развитие должно быть - это Mock-объекты. Без них можно писать TDD-тесты только для функций, которые принимают и возвращают простые типы: строки, целые, вещественные и т.д. А в ООП в функцию часто передаются объекты пользовательских классов. Я читал, что с помощью Mock-объектов эмулируют данные из сети, из базы данных и любые объекты пользовательских классов. Это удобно, если хочется потестить приложение, в котором реализована не вся функциональность (к примеру, сам разработчик или член команды ещё не закончил свою работу над какими-то классами), либо база данных не готова, либо она пустая, либо в базе данных нужны специфические данные, либо надо получить ответ на запрос с сайта (к примеру, комменты со стены в фейсбуке, которых нет), сайт может быть недоступен, к примеру, нет интернета или сайт ещё не разработан и т.д. На все эти ситуации ответ один - Mock-объекты. Позже напишу инструкцию по Google Mock: https://code.google.com/p/googlemock/