Зачем нужен cppunit?
От: Аноним  
Дата: 20.09.05 15:03
Оценка:
Здравствуйте, товарищи!

На днях пришлось долго отлаживать свою программу. В результате этого я задумался о важности тестов. Вдохновлённый протолжительностью отладки я скачал cppunit. После просмотра примеров у меня сложилось впечатление, что главное счастье от его использования — это умная директива ASSERT. В общем-то проверить, что 1=1 я и без этой программы мог. Очевидно, что я чего-то не понимаю.
Объясните мне, пожалуйста, в чём заключаются достоинства этой программы.
Заранее спасибо.
Re: Зачем нужен cppunit?
От: jazzer Россия Skype: enerjazzer
Дата: 20.09.05 15:05
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, товарищи!


А>На днях пришлось долго отлаживать свою программу. В результате этого я задумался о важности тестов. Вдохновлённый протолжительностью отладки я скачал cppunit. После просмотра примеров у меня сложилось впечатление, что главное счастье от его использования — это умная директива ASSERT. В общем-то проверить, что 1=1 я и без этой программы мог. Очевидно, что я чего-то не понимаю.

А>Объясните мне, пожалуйста, в чём заключаются достоинства этой программы.
А>Заранее спасибо.

Ты создаешь набор тестов, которые потом будешь прогонять автоматически, не задумываясь.
Или тебе охота каждый раз перед выпуском каждого релиза/патча вручную проверять, что в каждом модуле твоей программы по-прежнему 1==1 и ты ничего не сломал случайно?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Зачем нужен cppunit?
От: Аноним  
Дата: 20.09.05 16:32
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ты создаешь набор тестов, которые потом будешь прогонять автоматически, не задумываясь.

J>Или тебе охота каждый раз перед выпуском каждого релиза/патча вручную проверять, что в каждом модуле твоей программы по-прежнему 1==1 и ты ничего не сломал случайно?


Предположим, у меня вся логика задачи сидит в отдельной библиотеке. Я создаю проект тестер и в нём создаю много тестов, для проверки которых мне достаточно выполить одну функцию TestAll. Всё это я напишу сам без cppunit. Так зачем мне его ставить?
Re[3]: Зачем нужен cppunit?
От: Кодт Россия  
Дата: 20.09.05 17:06
Оценка: +4
Здравствуйте, Аноним, Вы писали:

J>>Ты создаешь набор тестов, которые потом будешь прогонять автоматически, не задумываясь.

J>>Или тебе охота каждый раз перед выпуском каждого релиза/патча вручную проверять, что в каждом модуле твоей программы по-прежнему 1==1 и ты ничего не сломал случайно?

А>Предположим, у меня вся логика задачи сидит в отдельной библиотеке. Я создаю проект тестер и в нём создаю много тестов, для проверки которых мне достаточно выполить одну функцию TestAll. Всё это я напишу сам без cppunit. Так зачем мне его ставить?


Чтобы велосипед не изобретать.

cppunit — это фреймворк, который
сам зарегистрирует все тесты, разбросанные по файлам тестового проекта
— не бабахнет на первом же ассерте, а аккуратно пробежит по всем тестам и расскажет, где, когда и с какими аргументами были assertion fault, а где были кинуты неожиданные исключения
— делает своё чёрное дело унифицированно; для командной работы это большой плюс.

Простота написания тестов позволяет нарожать их огромное количество.
За счёт этого можно найти и ошибки компиляции в том числе!
И разные эксперименты по рефакторингу проводить легко. Написал тест, поджёг, рвануло. Начинаешь думать — это ошибка в программе, или ошибка в твоём понимании программы. А почему она тогда возникла?..

Когда я с самоделок пересел на cppunit, то был поражён
Перекуём баги на фичи!
Re[4]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 21.09.05 09:57
Оценка: 1 (1)
Здравствуйте, Кодт, Вы писали:

К>Когда я с самоделок пересел на cppunit, то был поражён

С Boost.Test имеет смысл на cppunit переползать?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Зачем нужен cppunit?
От: Кодт Россия  
Дата: 21.09.05 10:05
Оценка:
Здравствуйте, Глеб Алексеев, Вы писали:

К>>Когда я с самоделок пересел на cppunit, то был поражён

ГА>С Boost.Test имеет смысл на cppunit переползать?

Не знаю, не пробовал boost/test. При случае заценю.
Перекуём баги на фичи!
Re[4]: Зачем нужен cppunit?
От: dad  
Дата: 21.09.05 10:07
Оценка:
К>Простота написания тестов позволяет нарожать их огромное количество.

а как быть с классами которые только в контексте gui выполняются? (а протестить тоже хотелось бы)
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re[5]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 21.09.05 10:14
Оценка:
Здравствуйте, dad, Вы писали:

dad>а как быть с классами которые только в контексте gui выполняются? (а протестить тоже хотелось бы)

Посмотри, может чего найдешь:
http://opensourcetesting.org/
Там какой-то GUITAR был, но я не смотрел.
Хочется верить в чудо, но вряд ли GUI просто тестируется автоматически.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: Зачем нужен cppunit?
От: dad  
Дата: 21.09.05 10:20
Оценка: :)
dad>>а как быть с классами которые только в контексте gui выполняются? (а протестить тоже хотелось бы)
ГА>Посмотри, может чего найдешь:
ГА>http://opensourcetesting.org/
ГА>Там какой-то GUITAR был, но я не смотрел.
ГА>Хочется верить в чудо, но вряд ли GUI просто тестируется автоматически.

я там все смотрел, просто думал, может ты обладаешь откровением :)
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re[7]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 21.09.05 10:23
Оценка:
Здравствуйте, dad, Вы писали:

ГА>>Посмотри, может чего найдешь:

ГА>>http://opensourcetesting.org/
dad>я там все смотрел, просто думал, может ты обладаешь откровением
Да где там, какие откровения
Я дальше Boost.Test (и простенького cxxunit, потому что Boost.Test на CE не работает) и не смотрел.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 10:33
Оценка: 14 (3)
Здравствуйте, Глеб Алексеев, Вы писали:

К>>Когда я с самоделок пересел на cppunit, то был поражён

ГА>С Boost.Test имеет смысл на cppunit переползать?

Когда искал себе unit-test-framework для С++ то выбирал между cppunit и Boost.Test. Остановился на Boost.Test, т.к.:
-- уж очень cppunit на JUnit похож. Boost.Test более C++like я бы сказал. Имхо, поэтому Boost.Test проще использовать (test-case легко можно делать обычными функциями, и так же легко использовать test-case в виде классов);
-- в Boost.Test больше вспомогательных макросов, что делает вставку ASSERT-ов гораздо читабельнее. Вот список boost-овских макросов из 1.32.0:

BOOST_BITWISE_EQUAL
BOOST_CHECK
BOOST_CHECK_CLOSE
BOOST_CHECK_EQUAL
BOOST_CHECK_EQUAL_COLLECTIONS
BOOST_CHECK_EXCEPTION
BOOST_CHECK_MESSAGE
BOOST_CHECK_NO_THROW
BOOST_CHECK_PREDICATE
BOOST_CHECK_THROW
BOOST_CHECKPOINT
BOOST_ERROR
BOOST_FAIL
BOOST_IS_DEFINED
BOOST_MESSAGE
BOOST_REQUIRE
BOOST_REQUIRE_MESSAGE
BOOST_REQUIRE_PREDICATE
BOOST_WARN
BOOST_WARN_MESSAGE

и аналогичный из cppunit:

CPPUNIT_ASSERT
CPPUNIT_ASSERT_ASSERTION_FAIL
CPPUNIT_ASSERT_ASSERTION_PASS
CPPUNIT_ASSERT_DOUBLES_EQUAL
CPPUNIT_ASSERT_EQUAL
CPPUNIT_ASSERT_EQUAL_MESSAGE
CPPUNIT_ASSERT_MESSAGE
CPPUNIT_ASSERT_NO_THROW
CPPUNIT_ASSERT_THROW

-- лично мне не нужны были GUI-инструменты (в cppunit что-то для этого есть).
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 10:40
Оценка:
Здравствуйте, eao197, Вы писали:

E>Когда искал себе unit-test-framework для С++ то выбирал между cppunit и Boost.Test. Остановился на Boost.Test, т.к.:

E>-- уж очень cppunit на JUnit похож.

А по мне, так это наоборот плюс. Потому как работать приходится много с чем, и наличие JUnit, CppUnit, NUnit не может не радовать.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[6]: Зачем нужен cppunit?
От: Cyberax Марс  
Дата: 21.09.05 10:41
Оценка: 8 (1) +2
eao197 wrote:

> К>>Когда я с самоделок пересел на cppunit, то был поражён

> ГА>С Boost.Test имеет смысл на cppunit переползать?
> Когда искал себе unit-test-framework для С++ то выбирал между cppunit
> и Boost.Test. Остановился на Boost.Test, т.к.:
> -- уж очень cppunit на JUnit похож. Boost.Test более C++like я бы сказал.

Мне больше всего понравился TUT — очень простой и как раз сделаный
С++-like.

http://tut-framework.sourceforge.net/

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 2.0 beta
Sapienti sat!
Re[4]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 10:43
Оценка:
Здравствуйте, Кодт, Вы писали:

К>- сам зарегистрирует все тесты, разбросанные по файлам тестового проекта

К>- не бабахнет на первом же ассерте, а аккуратно пробежит по всем тестам и расскажет, где, когда и с какими аргументами были assertion fault, а где были кинуты неожиданные исключения
К>- делает своё чёрное дело унифицированно; для командной работы это большой плюс.

А как же Test First?
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[5]: Зачем нужен cppunit?
От: Alxndr Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 21.09.05 10:55
Оценка: +1
Здравствуйте, kwas, Вы писали:

K>А как же Test First?


Думаю, все предыдущие рассуждения можно рассматривать в контексте <YouFavoriteUnitTestFramework> — акцент стоит не на cppunit, а на не самоделках, тыкскызыть.
ИМХО.
Re[6]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 11:51
Оценка:
Здравствуйте, Alxndr, Вы писали:

K>>А как же Test First?

A>Думаю, все предыдущие рассуждения можно рассматривать в контексте <YouFavoriteUnitTestFramework> — акцент стоит не на cppunit, а на не самоделках, тыкскызыть.

Меня, когда я увидел эту ветку, серьезно мучило желание спросить, а понимает ли вопрошающий что такое эти unit-тесты и для чего они используются. Но сдержался
Не, в принципе все верно, и даже в Test Infected описан такой подход: сначала что-то пишем, потом тестируем, если это что-то где-то ломается. Но, тут, как мне кажется, есть некоторая доля лукавства: код, приведенный в статье, один к одному совпадает с кодом, полученным с помощью Test Driven Development и описанным в книге Кента Бека. То есть, код, тестируемый в статье и преподносимый как Code Driven, на самом деле разработан с помощью TDD. Это и есть та доля лукавства, о которой я говорил (можно попытаться прикинуть и причины этого лукавства — например, популяризация). А вот следствия этого лукавства, как это часто бывает, идут далеко: как можно получить код, который легко тестировать? По-моему — только задумавшись об этом при написании кода. Людей, которые могут вот так, слету, написать красивый, расширяемый и легко поддающийся тестированию код я пока еще не встречал. Я бы такого человека назвал гением. Так их называет и Бек (не дословно): "TDD — для середняков. Гениям он не нужен, а тупицам — не поможет". Поэтому остается второй вариант — начинать собственно с тестов. Что в большинстве случаев и приводит к желаемым результатам.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[7]: Зачем нужен cppunit?
От: Кодт Россия  
Дата: 21.09.05 12:01
Оценка: +1
Здравствуйте, kwas, Вы писали:

K>Людей, которые могут вот так, слету, написать красивый, расширяемый и легко поддающийся тестированию код я пока еще не встречал. Я бы такого человека назвал гением. Так их называет и Бек (не дословно): "TDD — для середняков. Гениям он не нужен, а тупицам — не поможет". Поэтому остается второй вариант — начинать собственно с тестов. Что в большинстве случаев и приводит к желаемым результатам.


Гений может легко по невнимательности облажаться — как в архитектурных вопросах, так и в банальном кодинге.
Перекуём баги на фичи!
Re[8]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 12:08
Оценка: :))) :))) :))) :)
Здравствуйте, Кодт, Вы писали:

K>>Людей, которые могут вот так, слету, написать красивый, расширяемый и легко поддающийся тестированию код я пока еще не встречал. Я бы такого человека назвал гением. Так их называет и Бек (не дословно): "TDD — для середняков. Гениям он не нужен, а тупицам — не поможет". Поэтому остается второй вариант — начинать собственно с тестов. Что в большинстве случаев и приводит к желаемым результатам.


К>Гений может легко по невнимательности облажаться — как в архитектурных вопросах, так и в банальном кодинге.

+1

Так и хочется сказать: "Это точно. Вот помню один раз опечатался в коде..."
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[9]: Зачем нужен cppunit?
От: Cyberax Марс  
Дата: 21.09.05 12:19
Оценка: 4 (2) :))) :))) :))) :))) :))) :))
eao197 wrote:

> К>Гений может легко по невнимательности облажаться — как в

> архитектурных вопросах, так и в банальном кодинге.
> +1
> Так и хочется сказать: "Это точно. Вот помню один раз опечатался в
> коде..."

Вспомнился анекдот:

Richard M. Stallman, Linus Torvalds, and Donald E. Knuth engage in a
discussion on whose impact on Computer Science was the greatest.
Stallman: "God told me I have programmed the best editor in the world!"
Torvalds: "Well, God told *me* that I have programmed the best operating
system in the world!"
Knuth: "Wait, wait — I never said that."


--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 2.0 beta
Sapienti sat!
Re[7]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 12:22
Оценка:
Здравствуйте, kwas, Вы писали:

K>>>А как же Test First?

A>>Думаю, все предыдущие рассуждения можно рассматривать в контексте <YouFavoriteUnitTestFramework> — акцент стоит не на cppunit, а на не самоделках, тыкскызыть.

K>Меня, когда я увидел эту ветку, серьезно мучило желание спросить, а понимает ли вопрошающий что такое эти unit-тесты и для чего они используются. Но сдержался


А вот я как раз часто не понимаю... Имхо, unit-тесты хороши, когда есть какая-то библиотечка (той же сериализации, скажем) и нужно протестировать ее методы. Ну там сериализовать то-то, десериализовать, сравнить. Попробовать десериализовать то-то, посмотреть, что получилось. Это понятно. Это unit-тестами покрывается. Ну а дальше, когда библиотеки нужно в программу объединять? Вот есть у меня какой-нибудь агентик, который в каком-то состоянии на какое-то определенное воздействие определенным образом прореагировать должен. Причем агентик встраивается в какой-нибудь сложный фреймворк. Да и попадает в это состояние по сложной цепочке действий. И для моделирования всего этого приходится несколько процессов стартовать, определенные команды им выдавать. Вот в таких случаях чем пользоваться? Сейчас я специальные имитационные конфигурации делаю. Иногда вместо отдельных процессов специальные имитаторы пишутся. Но все запускается вручную. И проверяется так же вручную. Да и объем написаных unit-тестов оказывается настолько мал по сравнению с остальными работами. Ну т.е. не сказать, что совсем уж бесполезная штука (наоборот, свою задачу решает), но как бы это выразится -- мелкая по масштабу.

Все вышесказанное, естественно, ИМХО. Но интересно, что об этом другие участники форума думают.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[8]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 21.09.05 12:40
Оценка: +5
Здравствуйте, eao197, Вы писали:

E>Все вышесказанное, естественно, ИМХО. Но интересно, что об этом другие участники форума думают.

Сразу оговорюсь, что масштабы проектов, над которыми я работал, не позволяют мне авторитетно, тэкскть, высказаться.
Unit-test'ы — это самый нижний уровень. Есть еще functional, performance и прочие тесты. Например, в этом году пришлось писать XMLDA-сервис, и наличие готового test-suite — это была просто манна небесная.

Но даже сами по себе юнит-тесты — это гигантский прорыв. Почему? Из-за элементарной комбинаторики. Если в компоненте A 4 метода, нужно, грубо говоря, по одному тесту на каждый из них (в каждом тесте проверяются граничные и нормальный случаи). А вот если для этого компонента юнит-теста нет, то нужно тестировать все функции, в которых этот компонент используется, и ошибка в одном из граничных случаев одного из методов A будет в клиенте вылезать только в 1% случаев.

Прочувствовал это на последнем проекте, у нас, к сожалению, юнит-тесты не приняты, а также полно велосипедов (своя строка, свой вектор, свой ХМЛ-парсер , свои обертки над потоками/мутексами, своя реализация паттерна Обсервер, и т.д.). И сколько раз приходилось убивать часы в отладчике (расставлять трассировку по всему проекту), чтобы найти очередной глюк в строке . Мораль — если базовая библиотека хорошо покрыта элементарными юнит-тестами, остальную логику, в том числе ГУИ, можно с высокой степенью уверенности тестировать ручками и спать спокойно по ночам.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 12:57
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Гений может легко по невнимательности облажаться — как в архитектурных вопросах, так и в банальном кодинге.


Есть у меня такая неприятная особенность — не всегда договариваю то, что сказать хотел
А сказать хотел вот что:
Пусть
* A — вероятность нормальной Unit-тестируемости кода, написанного сходу гениями,
* B — вероятность нормальной тестируемости кода, написанного сходу середняками,
* С — вероятность нормальной тестируемости кода, написанного середняками с помощью TDD.

Только, прошу, не придирайтесь к классификации — она тут не причем. Так вот, я (и, как мне кажется, Бек тоже) считаю, что A ~= 2 * B и A ~= C. Вероятности эти — это мои собственные измышления, вполне возможно и неверные в корне. Как они соотносятся с количественными параметрами, типа процента покрытия тестами — не имею понятия, может быть вообще никак

Лажа гения в банальном кодинге отлавливается Unit-тестами, с учетом которых он и был написан Лажа же архитектурная... Ну, и на старуху бывает проруха, что уж тут поделаешь. Если ты своим сообщением хотел сказать, что даже гениям стоит писать UnitTest'ы и использовать TDD, то я с тобой соглашусь целиком и полностью (правда, это может прервать полет гениальной мысли, но разве это когда-нибудь останавливало Project Manager'ов )

И еще: я (читай IMHO) рассматриваю Unit Testing в первую очередь как средство проектирования. И только как бесплатное, но очень полезное и нужное дополнение — контроль корректности программы при внесении в нее изменений. Многие ставят во главу угла именно второе — это их право. Зависит от опыта и приоритетов, наверное.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[9]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 12:58
Оценка:
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Но даже сами по себе юнит-тесты — это гигантский прорыв.

Собственно, с этим я и не спорил. Просто мне интересно, а есть что-нибудь аналогичное, автоматизированное, но для более высокого уровня. Может кто чем-то пользуется. Интересны впечатления.

ГА>Прочувствовал это на последнем проекте, у нас, к сожалению, юнит-тесты не приняты, а также полно велосипедов (своя строка, свой вектор, свой ХМЛ-парсер , свои обертки над потоками/мутексами, своя реализация паттерна Обсервер, и т.д.). И сколько раз приходилось убивать часы в отладчике (расставлять трассировку по всему проекту), чтобы найти очередной глюк в строке . Мораль — если базовая библиотека хорошо покрыта элементарными юнит-тестами, остальную логику, в том числе ГУИ, можно с высокой степенью уверенности тестировать ручками и спать спокойно по ночам.


Так если ты считаешь, что unit-тесты должны быть, то настаивай на их внедрении.
А что касается велосипедов... Будучи сам патологическим велосипедоманом, хочу сказать, что есть порода такая -- велосипедостроители. Попадется вам вменяемый велосипедостроитель, которого получится в рамках удерживать -- будут у вас полезные и отлаженные велосипеды. А нет -- тогда действительно пиши пропало
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[10]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 13:15
Оценка:
Здравствуйте, eao197, Вы писали:

E>Так если ты считаешь, что unit-тесты должны быть, то настаивай на их внедрении.

E>А что касается велосипедов... Будучи сам патологическим велосипедоманом, хочу сказать, что есть порода такая -- велосипедостроители. Попадется вам вменяемый велосипедостроитель, которого получится в рамках удерживать -- будут у вас полезные и отлаженные велосипеды. А нет -- тогда действительно пиши пропало

Как проверить вменяемость велосипедостроителя? Вижу два пути:
а) наличие тестов,
б) вставка велосипеда в проект.

Тут тоже есть свои подвохи:
а) наличие тестов не гарантирует вменяемости, так как сами тесты могут оказаться невменяемыми — вырожденный случай, из категории "тупица",
б) тестов нет, велосипед пишется тут же, багов нет, все отлично — вырожденный случай, из категории "гений"
б) наличие тестов в наличии, но баги есть, так как тестами покрыли не все случаи — добавляем тест, "ночью спим спокойно" (с),
в) если велосипед без тестов работает, ничего не падает, то это также не гарантирует вменяемости, особенно если велосипедостроитель пришел с уже готовыми велосипедами. Потому как кто его знает, сколько бессонных ночей он провел в отладчике, вылизывая свой велосипед. И разрешив ему писать новые велосипеды, можно сильно ухудшить ситуацию.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[11]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 21.09.05 13:25
Оценка:
Здравствуйте, kwas, Вы писали:

K>Как проверить вменяемость велосипедостроителя? Вижу два пути:

K>а) наличие тестов,
K>б) вставка велосипеда в проект.

Вы смотрите на ситуацию со стороны начальника, т.е. велосипедостроитель — обязательно подчиненный. Я работаю в команде из 3х человек, выраженной иерархии у нас нет, но велосипедостроитель, не желающий слышать о юнит-тестах — старший товарищ, типа где-то даже начальник. Да, и такое бывает .
Поэтому я, когда пишу что-то явно библиотечной направленности (типа bicycle_lib::tree<T>), то добровольно рисую юнит-тест, если в нашей общей bicycle_lib обнаружился глюк, опять же рисую тестик для обнаружения этого глюка, но покрытие тестов очень низкое. Так вот, даже в этой ситуации юнит-тесты здорово помогают, при том, что никакого TDD и в помине нет.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[11]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 13:41
Оценка: 1 (1) +1
Здравствуйте, kwas, Вы писали:

E>>Так если ты считаешь, что unit-тесты должны быть, то настаивай на их внедрении.

E>>А что касается велосипедов... Будучи сам патологическим велосипедоманом, хочу сказать, что есть порода такая -- велосипедостроители. Попадется вам вменяемый велосипедостроитель, которого получится в рамках удерживать -- будут у вас полезные и отлаженные велосипеды. А нет -- тогда действительно пиши пропало

K>Как проверить вменяемость велосипедостроителя? Вижу два пути:

K>а) наличие тестов,
K>б) вставка велосипеда в проект.

Имхо, это совсем не то. Вменяемость -- это когда велосипедостроитель сам понимает, что для какого-то конкретного случая изобретение велосипеда не оправдано. Либо ему достаточно об этом один раз сказать. Либо он вовремя замечает, что разработка велосипеда слишком затягивается/дорожает. Невменяемый велосипедостроитель -- напротив, не признает доводов и настаивает на своем решении не взирая на разумные возражения и/или срыв сроков/бюджетов.


Наличие тестов (тем более unit-тестов), имхо, не точный показатель качества велосипеда. Здесь все зависит от процесса разработки, которого придерживается велосипедостроитель. Понятно, что какие-то тесты он проводит, просто не факт, что они остаются в виде исходников/bat/sh файлов. Unit-тесты -- это, скорее, показатель удобства сопровождения/портирования. Чем меньше готовых unit-тестов, которые можно на автомате прогнать, тем сложнее процедура портирования, т.к. все операции тестирования по памяти приходится повторять на новой платформе. Аналогично и с сопровождением. Без автоматических unit-тестов сложно убедится в том, что исправленная в одном месте запятая, не приведет к Access Violation в другом месте. Понятно, что все это сказывается на качестве, но, повторюсь, что выпущенная велосипедостроителем версия может быть вполне работоспособной и надежной даже без большого количества unit-тестов.

Что же касается вставки в проект, то здесь так же много своих факторов. Например, наличие/качество документации (что для велосипедов, вообще говоря, редкость). Наличие под рукой самого велосипедостроителя, чтобы можно было его каверзными вопросами помучать и заставить пару примеров написать. Ориентированность велосипеда на конкретную задачу -- ведь для чего-то же он создавался. И если окажется, что для пользователей велосипеда его использование в рамках конкретной задачи интуитивно понятно, то и внедрение пройдет без проблем.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[12]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 14:17
Оценка: +1
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Вы смотрите на ситуацию со стороны начальника, т.е. велосипедостроитель — обязательно подчиненный. Я работаю в команде из 3х человек, выраженной иерархии у нас нет, но велосипедостроитель, не желающий слышать о юнит-тестах — старший товарищ, типа где-то даже начальник. Да, и такое бывает .


Знаю, сам с таким постоянно сталкиваюсь, и постоянно пытаюсь сподвигнуть на boost (если, конечно, есть аналоги), кивая в первую очередь на оттестированность (как юнит-тестами, так и пользователями в проектах). Получается с переменным успехом

ГА>Поэтому я, когда пишу что-то явно библиотечной направленности (типа bicycle_lib::tree<T>), то добровольно рисую юнит-тест, если в нашей общей bicycle_lib обнаружился глюк, опять же рисую тестик для обнаружения этого глюка, но покрытие тестов очень низкое. Так вот, даже в этой ситуации юнит-тесты здорово помогают, при том, что никакого TDD и в помине нет.


С интерфейсом tree, на мой взгляд, довольно трудно напортачить, тем более если вы изобретатель со стажем Благо есть немало примеров этих самых интерфейсов. Согласен, для вещей, интерфейс которых предсказуем, если не сказать стандартен, ваш подход работает на 100%. А вот новая бизнес-логика, для которой библиотек нет в помине, или просто сторонние библиотеки — с ними что делать? Взять вот тот же пример eao197
Автор: eao197
Дата: 21.09.05
:

E> Имхо, unit-тесты хороши, когда есть какая-то библиотечка (той же сериализации, скажем) и нужно протестировать ее методы.

E> Ну там сериализовать то-то, десериализовать, сравнить. Попробовать десериализовать то-то, посмотреть, что получилось.

Так вот я считаю, что этого недостаточно, а чаще просто не нужно. Потому как, если библиотека, поставляемая без юнит-тестов, не делает элементарных вещей, для которых она предназначена, то пользователи тут же закидают авторов тухлыми яйцами (багрепортами). Более интересны как раз граничные случаи: что будет, если парсеру подсунуть .cpp файл вместо сериализованного файла? Что будет, если сериализованный файл попортится где-нибудь в серединке? Что будет, если попытаться десериализовать объект одного типа в объект другого типа, несовместимого с исходным? Вполне возможно, что eao197 о его тестировании таких случаев просто не счел нужным упомянуть, но я как раз их считаю более важными, и именно из бережного к ним отношения вырастает красивая архитектура.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[13]: Зачем нужен cppunit?
От: jazzer Россия Skype: enerjazzer
Дата: 21.09.05 14:23
Оценка: +1
Здравствуйте, kwas, Вы писали:

K>просто сторонние библиотеки — с ними что делать?


Обвязывать юнит-тестами.
Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[14]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 14:36
Оценка:
Здравствуйте, jazzer, Вы писали:

K>>просто сторонние библиотеки — с ними что делать?


J>Обвязывать юнит-тестами.

J>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма

А как, интересно, обвязать unit-тестами, например, iconv или libxml2?

Может лучше, если убеждаешься, что баг именно в библиотеке, написать один единственный тест, которые показывает наличие именно этого бага. И новые версии библиотек проверять этим тестом. И только когда появится новый баг, то написать для него отдельный тест.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[13]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 14:36
Оценка:
Здравствуйте, kwas, Вы писали:

<...>

K>Более интересны как раз граничные случаи: что будет, если парсеру подсунуть .cpp файл вместо сериализованного файла? Что будет, если сериализованный файл попортится где-нибудь в серединке? Что будет, если попытаться десериализовать объект одного типа в объект другого типа, несовместимого с исходным? Вполне возможно, что eao197 о его тестировании таких случаев просто не счел нужным упомянуть, но я как раз их считаю более важными, и именно из бережного к ним отношения вырастает красивая архитектура.


Эти случаи интересны, если библиотека претендует на выживаемость в таких условиях. Но, нужно сказать, что подобная надежность покупается избыточностью (либо увеличением сериализованного образа, либо времени сериализации/десериализации, либо и того, и другого). Что не для всех задач приемлимо. Хрустальная ваза должна быть красивой и не протекать. А выдерживать удары молотком или падения на пол вовсе не обязана.

Кроме того, имхо, архитектура к покрытию тестами не имеет отношения вообще.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[15]: Зачем нужен cppunit?
От: Кодт Россия  
Дата: 21.09.05 14:47
Оценка:
Здравствуйте, eao197, Вы писали:

E>А как, интересно, обвязать unit-тестами, например, iconv или libxml2?


E>Может лучше, если убеждаешься, что баг именно в библиотеке, написать один единственный тест, которые показывает наличие именно этого бага. И новые версии библиотек проверять этим тестом. И только когда появится новый баг, то написать для него отдельный тест.


Ну ты же не всем iconv'ом пользуешься, а некоторыми юз-кейсами. Обвязываешь их.
То есть, попросту, всё то, что ты обвязал бы ASSERT'ами внутри программы вокруг вызова iconv, — выносишь в тест. (Ассерты тоже можно оставить).
Перекуём баги на фичи!
Re[16]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 14:57
Оценка: 1 (1)
Здравствуйте, Кодт, Вы писали:

К>Ну ты же не всем iconv'ом пользуешься, а некоторыми юз-кейсами. Обвязываешь их.

К>То есть, попросту, всё то, что ты обвязал бы ASSERT'ами внутри программы вокруг вызова iconv, — выносишь в тест. (Ассерты тоже можно оставить).

Чесно говоря, так и не приобрел привычки ASSERT-ами пользоваться. Практически их не пишу. Да и если бы писал, то толку в них особого не было бы -- компилюсь обычно сразу в release-режиме, чтобы затем не бодаться с ситуациями, когда в debug работает, а в release -- нет.

Но все равно, кажется мне это параноидальным. Ведь я выбираю какую-то библиотеку для себя. Естественно, обращаю внимание на то, насколько широко она используется. Беру только stable-версии. Желательно постарше. Т.е. я расчитываю на то, что автор библиотеки и ее пользователи потратили достаточно времени, чтобы погонять ее.

Если же я сам начинаю делать тестовые обвязки вокруг библиотеки (даже не встретив реального бага), то я делаю работу, которую, с большой вероятностью, можно считать бесполезной. А ведь я мог бы потратить тоже самое время на свой прикладной код, на написание теста для него, к примеру.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[14]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 15:00
Оценка:
Здравствуйте, jazzer, Вы писали:

K>>просто сторонние библиотеки — с ними что делать?

J>Обвязывать юнит-тестами.
J>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма

Резонно. С ненужностью я погорячился. То есть, тривиальные операции тоже проверяем. И граничные случаи. И заведомо неправильные данные. И так для каждой библиотеки. Why me? Да понятно, почему — потому что мне это надо.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[15]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 15:09
Оценка:
Здравствуйте, kwas, Вы писали:

K>>>просто сторонние библиотеки — с ними что делать?

J>>Обвязывать юнит-тестами.
J>>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма

K>Резонно. С ненужностью я погорячился. То есть, тривиальные операции тоже проверяем. И граничные случаи. И заведомо неправильные данные. И так для каждой библиотеки. Why me? Да понятно, почему — потому что мне это надо.


Интересно, а strcat так же я должен у себя протестировать? Ведь использую.
Или Boost.Test должен я предварительно протестировать, прежде чем на его основе свои тесты писать?
Или Boost.MultiIndex или Boost.FileSystem?

Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[14]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 15:39
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, kwas, Вы писали:


E>Хрустальная ваза должна быть красивой и не протекать. А выдерживать удары молотком или падения на пол вовсе не обязана.


Но и исчезать беззвучно, аннигилируя все в округе в радиусе 1 км, ваза тоже не должна, не правда ли? Даже если ее бъют молотком. Вот и проверяется то, что ваза просто разобъется на осколки, разлетевшиеся на 2 метра во все стороны.

E>Кроме того, имхо, архитектура к покрытию тестами не имеет отношения вообще.


При тестировании постфактум — естественно. При TDD — имхо, имеет. TDD заставляет (меня, по крайней мере, точно) задумываться о граничных и неправильных входных данных. Это очень часто влияет на интерфейс тестируемого класса/метода, и, следовательно на архитектуру.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[15]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 15:53
Оценка:
Здравствуйте, kwas, Вы писали:

E>>Хрустальная ваза должна быть красивой и не протекать. А выдерживать удары молотком или падения на пол вовсе не обязана.


K>Но и исчезать беззвучно, аннигилируя все в округе в радиусе 1 км, ваза тоже не должна, не правда ли? Даже если ее бъют молотком. Вот и проверяется то, что ваза просто разобъется на осколки, разлетевшиеся на 2 метра во все стороны.


Имхо, если что-то пошло не так уж лучше как можно скорее грохнуться, да еще с максимально возможным шумом. Но не факт, что этот шум можно поднять. Последствия от брошенного, но не пойманного std::exception или от вызова abort() могут быть самыми разными. Причем, что важно, библиотека может позволить себе либо выбросить исключение, либо вызвать abort() в зависимости о того, насколько она оценивает тяжесть своего положения. Подобные вопросы поднимались в теме Checked exceptions... зло или добро?
Автор: mishaa
Дата: 16.07.05
, особенно в ветках Re[19]: Checked exceptions... зло или добро?
Автор: Павел Кузнецов
Дата: 18.07.05
и Re[22]: Checked exceptions... зло или добро?
Автор: VladD2
Дата: 22.07.05
.

E>>Кроме того, имхо, архитектура к покрытию тестами не имеет отношения вообще.


K>При тестировании постфактум — естественно. При TDD — имхо, имеет. TDD заставляет (меня, по крайней мере, точно) задумываться о граничных и неправильных входных данных. Это очень часто влияет на интерфейс тестируемого класса/метода, и, следовательно на архитектуру.


А примеры вот этого влияния TDD на архитектуру можно? А то я, например, отталкиваюсь от удобства использования инструмента в повседневной работе, а проблемы тестирования на втором плане.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[16]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 21.09.05 15:55
Оценка:
Здравствуйте, eao197, Вы писали:

K>>Резонно. С ненужностью я погорячился. То есть, тривиальные операции тоже проверяем. И граничные случаи. И заведомо неправильные данные. И так для каждой библиотеки. Why me? Да понятно, почему — потому что мне это надо.


E>Интересно, а strcat так же я должен у себя протестировать? Ведь использую.

E>Или Boost.Test должен я предварительно протестировать, прежде чем на его основе свои тесты писать?
E>Или Boost.MultiIndex или Boost.FileSystem?

У Boost есть unit-тесты (в том числе и для Boost.Test ) Я, собственно, к тому и вел (выше я опять не вовремя остановился ) — неблагодарное это дело, пользователю тестировать все используемые библиотеки. Unit-тесты должен предоставлять разработчик библиотеки, а не пользователь. В идеале наличие тестов должно влиять на принятие решения об использовании/не использовании библиотеки в проекте.

E>Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?


Вижу два: "Ранее уже пользовал, все нормально" и "А у нее свои тесты есть". Остальным теоретически доверять нельзя. Практически же — как повезет. Можно на код посмотреть, если есть, на документированность, и т.д.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[16]: Зачем нужен cppunit?
От: Кодт Россия  
Дата: 21.09.05 15:58
Оценка: +2
Здравствуйте, eao197, Вы писали:

E>Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?


А нигде. Помните ошибку в FPU первого Пня? Вот тоже все были уверены, что 2.0 * 2.0 = 4.0

Во-первых, есть людская молва и слава. Скажем, какие-то авторы славятся своей криворукостью (например, ваши партнёры из Индии или из соседнего кабинета).

Во-вторых, юнит-тесты — это способ освоения библиотеки. Любую документацию, по законам Мерфи, можно прочесть неправильно.
Вот появилась в вашем распоряжении сторонняя библиотека. Первым делом практикуешься с ней. Пишешь тест, от которого ждёшь такое-то поведение. Написал, запустил, обломился. Полез в документацию. Ага, это недопонимание... Переписал тест. Запустил, обломился. Ага, это багрепорт. И так далее.
Всё понял, всё работает... а тесты выкидывать не надо: пусть лежат до следующей версии.
Перекуём баги на фичи!
Re[17]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 21.09.05 20:21
Оценка:
Здравствуйте, Кодт, Вы писали:

E>>Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?


К>А нигде. Помните ошибку в FPU первого Пня? Вот тоже все были уверены, что 2.0 * 2.0 = 4.0


Так в том-то и дело. Есть ошибки, от которых бесполезно пытаться защититься. Это как падающие балконы -- в подавляющем большинстве они не падают, но прецеденты бывают

К>Во-вторых, юнит-тесты — это способ освоения библиотеки. Любую документацию, по законам Мерфи, можно прочесть неправильно.

К>Вот появилась в вашем распоряжении сторонняя библиотека. Первым делом практикуешься с ней. Пишешь тест, от которого ждёшь такое-то поведение. Написал, запустил, обломился. Полез в документацию. Ага, это недопонимание... Переписал тест. Запустил, обломился. Ага, это багрепорт. И так далее.
К>Всё понял, всё работает... а тесты выкидывать не надо: пусть лежат до следующей версии.

Честно говоря, не нравится мне знакомиться с библиотекой по ее unit-тестам. Имхо, у библиотеки должны быть тесты (tests, unittests) и примеры (sample, examples). Первые предназначены для разработчиков библиотеки. Там может и, в принципе, должно быть погружение в такие дебри, которые обычному пользователю библиотеки и знать-то не обязательно. Как правильно сказал kwas -- проверки на всякие граничные условия и экстремальные режимы
Автор: kwas
Дата: 21.09.05
. А вот примеры должны показывать как нужно пользоваться библиотекой в штатном режиме, без экстрима.

Вот при наличии примеров изучение новой библиотеки для меня идет по другому, чем ты описал, сценарию:
-- читаю документацию (да, в отличии от настоящих программистов, стараюсь читать документацию до написания кода );
-- пытаюсь разобраться в примере;
-- при отсутствии понимания возвращаюсь к чтению документации;
-- начинаю курочить примеры подгоняя их под то, что нужно мне в моей задаче;
-- при достижении полного понимания того, что мне нужно, пишу чистовой код в своем проекте.

А раскуроченные примеры из состава библиотеки выглядят, обычно, как совершенно нечитабельные черновики. И пылятся себе где-нибудь в дебрях ~/tmp/some-lib/t43.

Вот, в качестве иллюстрации того, почему мне не нравится изучать библиотеки по unit-тестам, пример теста ACE_Date_Time из ACE. Для того, чтобы понять, как этим классом пользоваться в примере (sample) достаточно было бы написать всего-то выделенный жирным фрагмент. А все остальное -- это оверхед, который важен при тестировании, но совершенно не нужен при изучении библиотеки.
// Date_Time_Test.cpp,v 4.6 2003/12/02 19:35:10 shuston Exp

// ============================================================================
//
// = LIBRARY
//    tests
//
// = DESCRIPTION
//    This program verifies the ACE_Date_Time class.
//
// = AUTHOR
//    Steve Huston  <shuston@riverace.com>
//
// ============================================================================

#include "ace/Date_Time.h"
#include "test_config.h"

ACE_RCSID(tests, Date_Time_Test, "Date_Time_Test.cpp,v 4.6 2003/12/02 19:35:10 shuston Exp")

static ACE_Date_Time static_dt;   // Making sure it doesn't crash.

int
run_main (int, ACE_TCHAR *[])
{
  ACE_START_TEST (ACE_TEXT ("Date_Time_Test"));

  int error = 0;

  ACE_Date_Time dt;

  long month   = dt.month ();
  long day     = dt.day ();
  long year    = dt.year ();
  long hour    = dt.hour ();
  long minute  = dt.minute ();
  long seconds = dt.second ();
  long usec    = dt.microsec ();
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("ACE_Date_Time (m/d/y, h:m:s.u): %d/%d/%d, %d:%d:%d.%d\n"),
              month, day, year, hour, minute, seconds, usec));
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("ACE_Log thinks it is: %D\n")));

  if (month < 1 || month > 12)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Month (%d) out of range (1-12)\n"),
                  month));
      error = 1;
    }
  if (day < 1 || day > 31)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Day (%d) out of range (1-31)\n"),
                  day));
      error = 1;
    }
  if (year < 1900 || year > 2100)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Year (%d) out of range (1900-2100)\n"),
                  year));
      error = 1;
    }
  if (hour < 0 || hour > 23)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Hour (%d) out of range (0-23)\n"),
                  hour));
      error = 1;
    }
  if (minute < 0 || minute > 59)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Minute (%d) out of range (0-59)\n"),
                  minute));
      error = 1;
    }
  if (seconds < 0 || seconds > 59)
    {
      ACE_ERROR ((LM_ERROR, ACE_TEXT ("Seconds (%d) out of range (0-59)\n"),
                  seconds));
      error = 1;
    }
  if (usec < 0 || usec > 999999)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Microseconds (%d) out of range (0-999999)\n"),
                  usec));
      error = 1;
    }

  // The static ACE_Date_Time object is primarily to be sure it doesn't
  // crash; However, let's do some sanity checks on it to be sure it's
  // legit as well.
  if (static_dt.month () != month)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Static month (%d) doesn't match %d\n"),
                  static_dt.month (), month));
      error = 1;
    }
  if (static_dt.day () != day)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Static day (%d) doesn't match %d\n"),
                  static_dt.day (), day));
      error = 1;
    }
  if (static_dt.year () != year)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Static year (%d) doesn't match %d\n"),
                  static_dt.year (), year));
      error = 1;
    }
  if (static_dt.hour () != hour)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Static hour (%d) doesn't match %d\n"),
                  static_dt.hour (), hour));
      error = 1;
    }
  if (static_dt.minute () != minute)
    {
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT ("Static minute (%d) doesn't match %d\n"),
                  static_dt.minute (), minute));
      error = 1;
    }
  ACE_END_TEST;
  return error;
}
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[18]: Зачем нужен cppunit?
От: Зверёк Харьковский  
Дата: 22.09.05 00:15
Оценка:
Здравствуйте, eao197, Вы писали:

E>Вот, в качестве иллюстрации того, почему мне не нравится изучать библиотеки по unit-тестам, пример теста ACE_Date_Time из ACE. Для того, чтобы понять, как этим классом пользоваться в примере (sample) достаточно было бы написать всего-то выделенный жирным фрагмент. А все остальное -- это оверхед, который важен при тестировании, но совершенно не нужен при изучении библиотеки.


Тут, имхо, просто хреновый код юнит-тестирования. Хороший код будет выглядеть декларативно — как описание, какие контракты поддерживает библиотека. Грубо говоря (недо-код бустоподобный):

    ACE_Date_Time dt;
    long month   = dt.month ();
    long day     = dt.day ();
    long year    = dt.year ();
    long hour    = dt.hour ();
    long minute  = dt.minute ();
    long seconds = dt.second ();
    long usec    = dt.microsec ();

    /*
    ...
    */

    BOOST_CHECK(month >= 1 && month <= 12);
    BOOST_CHECK(day >= 1 && day <= 31);
    BOOST_CHECK(year >= 1900 && year <= 2100);
    BOOST_CHECK(hour >= 0 && hour <= 23);
    BOOST_CHECK(minute >= 0 && minute <= 59);
    BOOST_CHECK(seconds >= 0 && seconds <= 59);
    BOOST_CHECK(usec >= 0 && usec <= 999999);
    
    //static
    BOOST_CHECK(static_dt.month () == month)
    BOOST_CHECK(static_dt.day () == day)
    BOOST_CHECK(static_dt.year () == year)
    BOOST_CHECK(static_dt.hour () == hour)
    BOOST_CHECK(static_dt.minute () == minute)
E>


И в таком виде этот код отнюд не бесполезен. Он отвечает на не совсем тривиальные вопросы типа "месяца, дни, часы считаем от 0 или от 1?", "каковы допустимые значени лет?" и т.п.

А так же утверждает, что в static_dt хранятся значения, совпадающие с dt, полученным при помощи конструктора без параметров (текущая дата, видимо?)
FAQ — це мiй ай-кью!
Re[19]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.09.05 05:05
Оценка:
Здравствуйте, Зверёк Харьковский, Вы писали:

E>>Вот, в качестве иллюстрации того, почему мне не нравится изучать библиотеки по unit-тестам, пример теста ACE_Date_Time из ACE. Для того, чтобы понять, как этим классом пользоваться в примере (sample) достаточно было бы написать всего-то выделенный жирным фрагмент. А все остальное -- это оверхед, который важен при тестировании, но совершенно не нужен при изучении библиотеки.


ЗХ>Тут, имхо, просто хреновый код юнит-тестирования.

+1
Имхо, такие вещи сплошь и рядом встречаются.

ЗХ> Хороший код будет выглядеть декларативно — как описание, какие контракты поддерживает библиотека. Грубо говоря (недо-код бустоподобный):


ЗХ>
<...код поскипан...>
E>>


ЗХ>И в таком виде этот код отнюд не бесполезен. Он отвечает на не совсем тривиальные вопросы типа "месяца, дни, часы считаем от 0 или от 1?", "каковы допустимые значени лет?" и т.п.


ЗХ>А так же утверждает, что в static_dt хранятся значения, совпадающие с dt, полученным при помощи конструктора без параметров (текущая дата, видимо?)


Такие вещи, имхо, гораздо лучше видеть в документации.

Кроме того, и ACE, и твой вариант в качестве unit-теста не проверяет достаточно важных вещей, например, количество дней в феврале. Т.е. и как пример избыточен. И как unit-тест недостаточен.

Вот еще один unit-тест, уже моего производства, который как раз декларативен (с использованием Boost.Test) и делает простую вещь: если объект был запакован, затем распакован, потом опять запакован и распакован, то все первоначально заданные значения останутся неизменными. Простой тест, но разбираться по нему с библиотекой лично я бы не хотел, т.к. в большинстве случаев ее использование гораздо проще (наиболее часто использующиеся методы выделены):
// Тестирование не пустого объекта submit_sm_t.
void
test_submit_sm()
{
    const std::string service_type( "Test" );
    const oess_1::uchar_t source_addr_ton = 0x01;
    const oess_1::uchar_t source_addr_npi = 0x02;
    const std::string source_addr( "542" );
    const oess_1::uchar_t dest_addr_ton = 0x03;
    const oess_1::uchar_t dest_addr_npi = 0x04;
    const std::string dest_addr( "79030000000" );
    const oess_1::uchar_t esm_class = 0x05;
    const oess_1::uchar_t protocol_id = 0x06;
    const oess_1::uchar_t priority_flag = 0x07;
    const std::string schedule_delivery_time( "040219163000000R" );
    const std::string validity_period( "040219163000000R" );
    const oess_1::uchar_t registered_delivery = 0x08;
    const oess_1::uchar_t replace_if_present = 0x09;
    const oess_1::uchar_t data_coding = 0x10;
    const oess_1::uchar_t sm_default_msg_id = 0x11;
    const std::string message( "This is a test message" );

    const unsigned int submit_sm_len =
        1 + service_type.length() + // service_type.
        1 + // source_addr_ton.
        1 + // source_addr_npi.
        1 + source_addr.length() + // source_addr.
        1 + // dest_addr_ton.
        1 + // dest_addr_npi.
        1 + dest_addr.length() + // dest_addr.
        1 + // esm_class.
        1 + // protocol_id.
        1 + // priority_flag.
        1 + schedule_delivery_time.length() + // schedule_delivery_time.
        1 + validity_period.length() + // validity_period.
        1 + // registered_delivery.
        1 + // replace_if_present.
        1 + // data_coding.
        1 + // sm_default_msg_id.
        1 + message.length(); // sm_length.

    smpp_pdu_1::submit_sm_t submit1;
    submit1.set_service_type( service_type );
    submit1.set_source_addr_ton( source_addr_ton );
    submit1.set_source_addr_npi( source_addr_npi );
    submit1.set_source_addr( source_addr );
    submit1.set_dest_addr_ton( dest_addr_ton );
    submit1.set_dest_addr_npi( dest_addr_npi );
    submit1.set_dest_addr( dest_addr );
    submit1.set_esm_class( esm_class );
    submit1.set_protocol_id( protocol_id );
    submit1.set_priority_flag( priority_flag );
    submit1.set_schedule_delivery_time( schedule_delivery_time );
    submit1.set_validity_period( validity_period );
    submit1.set_registered_delivery( registered_delivery );
    submit1.set_replace_if_present_flag( replace_if_present );
    submit1.set_data_coding( data_coding );
    submit1.set_sm_default_msg_id( sm_default_msg_id );
    submit1.set_short_message( message );

    BOOST_CHECK_EQUAL( submit1.service_type(), service_type );
    BOOST_CHECK_EQUAL( submit1.source_addr_ton(), source_addr_ton );
    BOOST_CHECK_EQUAL( submit1.source_addr_npi(), source_addr_npi );
    BOOST_CHECK_EQUAL( submit1.source_addr(), source_addr );
    BOOST_CHECK_EQUAL( submit1.dest_addr_ton(), dest_addr_ton );
    BOOST_CHECK_EQUAL( submit1.dest_addr_npi(), dest_addr_npi );
    BOOST_CHECK_EQUAL( submit1.dest_addr(), dest_addr );
    BOOST_CHECK_EQUAL( submit1.esm_class(), esm_class );
    BOOST_CHECK_EQUAL( submit1.protocol_id(), protocol_id );
    BOOST_CHECK_EQUAL( submit1.priority_flag(), priority_flag );
    BOOST_CHECK_EQUAL( submit1.schedule_delivery_time(),
        schedule_delivery_time );
    BOOST_CHECK_EQUAL( submit1.validity_period(), validity_period );
    BOOST_CHECK_EQUAL( submit1.registered_delivery(),
        registered_delivery );
    BOOST_CHECK_EQUAL( submit1.replace_if_present_flag(),
        replace_if_present );
    BOOST_CHECK_EQUAL( submit1.data_coding(), data_coding );
    BOOST_CHECK_EQUAL( submit1.sm_default_msg_id(), sm_default_msg_id );
    BOOST_CHECK_EQUAL( submit1.short_message(), message );

    smpp_pdu_1::pdu_t pdu;
    submit1.write_to( pdu, 715 );

    BOOST_CHECK_EQUAL( pdu.command_id(),
        smpp_pdu_1::submit_sm_t::command_id );
    BOOST_CHECK_EQUAL( pdu.command_status(), 0 );
    BOOST_CHECK_EQUAL( pdu.sequence_number(), 715 );
    BOOST_CHECK_EQUAL( pdu.data_len(), submit_sm_len );

    oess_1::io::mem_buf_t buf;
    buf << pdu;

    BOOST_CHECK_EQUAL( buf.size(), submit_sm_len +
        smpp_pdu_1::command_header_t::bin_image_size );

    buf.set_pos( 0 );
    smpp_pdu_1::pdu_t pdu2;
    buf >> pdu2;

    BOOST_CHECK_EQUAL( pdu.command_id(), pdu2.command_id() );
    BOOST_CHECK_EQUAL( pdu.command_status(), pdu2.command_status() );
    BOOST_CHECK_EQUAL( pdu.sequence_number(), pdu2.sequence_number() );
    BOOST_CHECK_EQUAL( pdu.data_len(), pdu2.data_len() );

    smpp_pdu_1::submit_sm_t submit2( pdu2 );

    BOOST_CHECK_EQUAL( cpp_util_2::slexcast( submit1 ),
        cpp_util_2::slexcast( submit2 ) );

    BOOST_CHECK_EQUAL( submit2.service_type(), service_type );
    BOOST_CHECK_EQUAL( submit2.source_addr_ton(), source_addr_ton );
    BOOST_CHECK_EQUAL( submit2.source_addr_npi(), source_addr_npi );
    BOOST_CHECK_EQUAL( submit2.source_addr(), source_addr );
    BOOST_CHECK_EQUAL( submit2.dest_addr_ton(), dest_addr_ton );
    BOOST_CHECK_EQUAL( submit2.dest_addr_npi(), dest_addr_npi );
    BOOST_CHECK_EQUAL( submit2.dest_addr(), dest_addr );
    BOOST_CHECK_EQUAL( submit2.esm_class(), esm_class );
    BOOST_CHECK_EQUAL( submit2.protocol_id(), protocol_id );
    BOOST_CHECK_EQUAL( submit2.priority_flag(), priority_flag );
    BOOST_CHECK_EQUAL( submit2.schedule_delivery_time(),
        schedule_delivery_time );
    BOOST_CHECK_EQUAL( submit2.validity_period(), validity_period );
    BOOST_CHECK_EQUAL( submit2.registered_delivery(),
        registered_delivery );
    BOOST_CHECK_EQUAL( submit2.replace_if_present_flag(),
        replace_if_present );
    BOOST_CHECK_EQUAL( submit2.data_coding(), data_coding );
    BOOST_CHECK_EQUAL( submit2.sm_default_msg_id(), sm_default_msg_id );
    BOOST_CHECK_EQUAL( submit2.short_message(), message );
}
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[20]: Зачем нужен cppunit?
От: Зверёк Харьковский  
Дата: 22.09.05 06:09
Оценка:
Здравствуйте, eao197, Вы писали:

ЗХ>> Хороший код будет выглядеть декларативно — как описание, какие контракты поддерживает библиотека. Грубо говоря (недо-код бустоподобный):


ЗХ>>
E><...код поскипан...>
E>>>


ЗХ>>И в таком виде этот код отнюд не бесполезен. Он отвечает на не совсем тривиальные вопросы типа "месяца, дни, часы считаем от 0 или от 1?", "каковы допустимые значени лет?" и т.п.


ЗХ>>А так же утверждает, что в static_dt хранятся значения, совпадающие с dt, полученным при помощи конструктора без параметров (текущая дата, видимо?)


E>Такие вещи, имхо, гораздо лучше видеть в документации.


E>Кроме того, и ACE, и твой вариант в качестве unit-теста не проверяет достаточно важных вещей, например, количество дней в феврале. Т.е. и как пример избыточен. И как unit-тест недостаточен.


Хмы... Да, возможно, возможно.

E>Вот еще один unit-тест, уже моего производства, который как раз декларативен (с использованием Boost.Test) и делает простую вещь: если объект был запакован, затем распакован, потом опять запакован и распакован, то все первоначально заданные значения останутся неизменными. Простой тест, но разбираться по нему с библиотекой лично я бы не хотел, т.к. в большинстве случаев ее использование гораздо проще (наиболее часто использующиеся методы выделены)

[...]
Гхмы... Честно говоря, мне этот код вообще не нравится
Идиосинкразия к объектам с большим количеством полей.
FAQ — це мiй ай-кью!
Re[21]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.09.05 06:36
Оценка:
Здравствуйте, Зверёк Харьковский, Вы писали:

E>>Вот еще один unit-тест, уже моего производства, который как раз декларативен (с использованием Boost.Test) и делает простую вещь: если объект был запакован, затем распакован, потом опять запакован и распакован, то все первоначально заданные значения останутся неизменными. Простой тест, но разбираться по нему с библиотекой лично я бы не хотел, т.к. в большинстве случаев ее использование гораздо проще (наиболее часто использующиеся методы выделены)

ЗХ>[...]
ЗХ>Гхмы... Честно говоря, мне этот код вообще не нравится
ЗХ>Идиосинкразия к объектам с большим количеством полей.

Чтож поделать, предметная область такая. Это всего лишь статические (обязательные) поля одного типа PDU в протоколе SMPP. К ним можно приложить еще N необязательных TLV-полей.

Кроме того, мой опыт показал, что лучше сделать точную обертку вокруг PDU со всеми setter-/getter-ами, чем сразу писать упрощенные функции по формированию PDU. Просто очень быстро сталкиваешься с ситуациями, когда какому-то полю нужно назначить какое-нибудь экзотическое значение. А не можешь, т.к. упрощенная функция этого не позволяет. А уж если написал обертку с множеством setter-ов/getter-ов, то проверять их нужно все, иначе из-за copy-paste обязятельно где-то ошибка будет (хотя лучше в таких случаях код оберток через кодогенерацию получать, как вот здесь: Re: Использование метаданных в программах на языке C++
Автор: eao197
Дата: 08.09.05
).
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[22]: Зачем нужен cppunit?
От: Зверёк Харьковский  
Дата: 22.09.05 06:54
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, Зверёк Харьковский, Вы писали:


E>>>Вот еще один unit-тест, уже моего производства, который как раз декларативен (с использованием Boost.Test) и делает простую вещь: если объект был запакован, затем распакован, потом опять запакован и распакован, то все первоначально заданные значения останутся неизменными. Простой тест, но разбираться по нему с библиотекой лично я бы не хотел, т.к. в большинстве случаев ее использование гораздо проще (наиболее часто использующиеся методы выделены)

ЗХ>>[...]
ЗХ>>Гхмы... Честно говоря, мне этот код вообще не нравится
ЗХ>>Идиосинкразия к объектам с большим количеством полей.

E>Чтож поделать, предметная область такая. Это всего лишь статические (обязательные) поля одного типа PDU в протоколе SMPP. К ним можно приложить еще N необязательных TLV-полей.


E>Кроме того, мой опыт показал, что лучше сделать точную обертку вокруг PDU со всеми setter-/getter-ами, чем сразу писать упрощенные функции по формированию PDU. Просто очень быстро сталкиваешься с ситуациями, когда какому-то полю нужно назначить какое-нибудь экзотическое значение. А не можешь, т.к. упрощенная функция этого не позволяет. А уж если написал обертку с множеством setter-ов/getter-ов, то проверять их нужно все, иначе из-за copy-paste обязятельно где-то ошибка будет (хотя лучше в таких случаях код оберток через кодогенерацию получать, как вот здесь: Re: Использование метаданных в программах на языке C++
Автор: eao197
Дата: 08.09.05
).


Лезть с моим рылом в калашный ряд — конечно, некомильфо... Но, так, навскидку.
Я бы отделил тестирование setter/getter от тестирования корректной запаковки-распаковки. Что-то типа
smpp_pdu_1::submit_sm_t submit1(/*конструируется по куче значений*/);
//тестирование корректности setter/getter - в отдельную функцию

smpp_pdu_1::pdu_t pdu;

submit1.write_to( pdu, 715 );
//тестирование корректности pdu - в еще одну отдельную функцию

oess_1::io::mem_buf_t buf;
buf << pdu;

buf.set_pos( 0 );
smpp_pdu_1::pdu_t pdu2;
buf >> pdu2;

smpp_pdu_1::submit_sm_t submit2( pdu2 );

//и наконец:
BOOST_CHECK_EQUAL(submit1, submit2); //voila


Ну, по крайней мере, я обычно стараюсь юнит-тесты сделать а) атомарными и б) таки документирующими.
Здесь, например, корректность setter/getter — более "служебный" юнит-тест (документировать он ничего не документирует), а вот запаковка-распаковка — unit test и use case в одном лице. Как-то так.
FAQ — це мiй ай-кью!
Re[23]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 22.09.05 08:09
Оценка:
Здравствуйте, Зверёк Харьковский, Вы писали:

ЗХ>Лезть с моим рылом в калашный ряд — конечно, некомильфо... Но, так, навскидку.

ЗХ>Я бы отделил тестирование setter/getter от тестирования корректной запаковки-распаковки.

Имхо, здесь так открывается больший простор для утечки ошибок. Во-первых, ошибки могут быть в getter-ах/setter-ах (т.е. setter пишет в один атрибут, а getter читает из другого). Во-вторых, ошибки могут быть в упаковке/распаковке (например, какой-то атрибут забывается при упаковке и распаковке). Поэтому тестирование упаковки/распаковки без предварительного множества set-ов (чтобы избавиться от значений по умолчанию) все равно не обойдется. Кроме того код:
//и наконец:
BOOST_CHECK_EQUAL(submit1, submit2); //voila

подразуемевает наличие у объекта submit_sm_t оператора стравнения, которого нет (и вряд ли он здесь должен быть по специфике задачи).

ЗХ>Ну, по крайней мере, я обычно стараюсь юнит-тесты сделать а) атомарными и б) таки документирующими.


А я стараюсь делать юнит-тесты наиболее полными (если терпение позволяет) и не забочусь об их документировании. А вот use-case стараюсь описывать в examples с комментариями и прочим. А затем включаю эти examples в doxygen-документацию. Таким образом получаю:
— дополнительные тесты (т.к. examples как раз можно рассматривать как тесты типичных use-case);
— наглядные примеры, не замусоренные лишними проверками;
— документацию, в которой есть кросс-ссылки на примеры, а из примеров -- ссылки на описания классов/методов (как здесь, к примеру).

Такой подход, имхо, чуть более трудоемок для разработчика, зато проще для пользователя. Т.к. тесты, имхо, это детали реализации библиотеки. А вот готовые примеры -- это ее лицо, фактически, public interface.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Немного практики.
От: FreshMeat Россия http://www.rsdn.org
Дата: 22.09.05 09:00
Оценка: 47 (4)
Здравствуйте, Аноним, Вы писали:
А>...

А зачем вообще нужна любая библиотека?
Думаю, тебе будет любопытно почитать – письмо от Михаила Лепахина, в котором он описал подход работы с юнит тестами в www.mentor.com ( вопросы задавал, когда понимание предмета было на нулевом уровне)
Хорошо там, где мы есть! :)
Re[6]: Зачем нужен cppunit?
От: Аноним  
Дата: 22.09.05 09:47
Оценка: 1 (1)
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Хочется верить в чудо, но вряд ли GUI просто тестируется автоматически.


Тестируется достаточно просто и автоматически.
По крайней мере по черному ящику точно без особых проблем и чудес.
С помощью SilkTest'а или подобных продуктов это все решаемо
Re[7]: Зачем нужен cppunit?
От: _Winnie Россия C++.freerun
Дата: 22.09.05 10:28
Оценка:
Ы. Как я отстал от жизни. Мне почему-то хватает обычного (переопределенного) assert и небольшой програмки, которая компилирует и запускает все тесты из папки tests.
Так как срабатываение какого-то assert это экстраординарное событие, и с ним надо немедленно разбираться, то никаких проблем с тем, что при одном профейленном тесте остальные не работают, нет.

PS. А в чем пойнт использования макроса TEST_EQUAL(a, b) вместо TEST(a==b)?
Правильно работающая программа — просто частный случай Undefined Behavior
Re[8]: Зачем нужен cppunit?
От: Глеб Алексеев  
Дата: 22.09.05 10:36
Оценка: +1
Здравствуйте, _Winnie, Вы писали:

_W>Ы. Как я отстал от жизни. Мне почему-то хватает обычного (переопределенного) assert и небольшой програмки, которая компилирует и запускает все тесты из папки tests.

_W>Так как срабатываение какого-то assert это экстраординарное событие, и с ним надо немедленно разбираться, то никаких проблем с тем, что при одном профейленном тесте остальные не работают, нет.
Одно изменение может починить 5 тест-кейзов и поломать других 11.
Количество срабатывающих тест-кейзов — хороший индикатор прогресса.
При изменении версий, редизайне (да и просто при подходе test first) — проваленные тесты не экстраординарное событие, а норма.
Советую из интереса почитать Boost developers mailing list перед последним релизом, особенно regression notification's от Дуга Грегора, и сделать вывод — полезно это или нет.

_W>PS. А в чем пойнт использования макроса TEST_EQUAL(a, b) вместо TEST(a==b)?

В выводе сообщения: test a==b failed, was 3, expected 0
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[8]: Зачем нужен cppunit?
От: Аноним  
Дата: 22.09.05 10:56
Оценка:
Здравствуйте, _Winnie, Вы писали:

_W>PS. А в чем пойнт использования макроса TEST_EQUAL(a, b) вместо TEST(a==b)?


Чтобы отделить ожидаемое значение от полученного.
Re[5]: Зачем нужен cppunit?
От: Чипсет Россия http://merlinko.com
Дата: 22.09.05 16:41
Оценка:
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Здравствуйте, Кодт, Вы писали:


К>>Когда я с самоделок пересел на cppunit, то был поражён

ГА>С Boost.Test имеет смысл на cppunit переползать?

А в свете последних веяний о включении буста в стандарта, имхо, лучше переползать на буст, со сторонних инструментов...
... << А писал я этот бред на RSDN@Home 1.1.4 stable rev. 510, под звуки Estatic Fear — Chapter IV>>
"Всё что не убивает нас, делает нас сильнее..."
Re[16]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 26.09.05 13:10
Оценка:
Здравствуйте, eao197, Вы писали:

E>Имхо, если что-то пошло не так уж лучше как можно скорее грохнуться, да еще с максимально возможным шумом. Но не факт, что этот шум можно поднять. Последствия от брошенного, но не пойманного std::exception или от вызова abort() могут быть самыми разными. Причем, что важно, библиотека может позволить себе либо выбросить исключение, либо вызвать abort() в зависимости о того, насколько она оценивает тяжесть своего положения.


На самом деле, последствия от непроверенного кода возврата могут быть не легче... Вопрос только в документированности и ответственности пользователя библиотеки. Checked exceptions в C++ действительно могут привести к проблемам, но ведь можно не указывать их в декларации функции, а просто описать в документации к ней. А в unit-тестах проверять их наличие в соответствующих ситуациях.

K>>При тестировании постфактум — естественно. При TDD — имхо, имеет. TDD заставляет (меня, по крайней мере, точно) задумываться о граничных и неправильных входных данных. Это очень часто влияет на интерфейс тестируемого класса/метода, и, следовательно на архитектуру.

E>А примеры вот этого влияния TDD на архитектуру можно?

Попробую. Подозреваю, что выйдет достаточно натянуто
Ну, например, взять какой-нибудь парсер, который что-либо читает из файла и разбирает это что-то. Тривиальная реализация:

class ParserTest {
    void testParsedValue () {
        Parser parser;
        parser.parse ("filename.txt");
        assertFalse (parsedValue == "");
    }
};

class Parser {
public:
    void parse (string fileName);
    const string &parsedValue ();
};


Ага, а теперь потестируем граничные значения:

class ParserTest {
    void testParsedValue () {
        Parser parser;
        parser.parse ("filename.txt");
        assertFalse (parser.parsedValue () == "");
    }

    void testBadFiles () {
        Parser parser;
        assertException (parser.parse ("nonexistant.txt"), FileNotFoundException);
        assertException (parser.parse("badfile.exe"), BadFileException);
    }
};


Выясняется, что в testBadFiles() тестируются две различные вещи: проверка наличия файла и способность его правильно распарсить. Логично было бы их разнести.

class InputSourceTest {
public:
     void testValidInputSource () {
          const InputSource source = InputSource::create ("filename.txt");
          assertFalse (source.getContent () == "");
     }

     void testInvalidInputSource () {
          assertException (InputSource::create ("nonexistant"), FileNotFoundException);
     }
};

class InputSource {
public:
     static InputSource create (string fileName);
     const string &getContents ();
};

class ParserTest {
    void testParsedValue () {
        InputSource validSource = InputSource::create ("filename.txt");
        Parser parser;
        parser.parse (validSource);
        assertFalse (parser.parsedValue () == "");
    }

    void testBadSource () {
        InputSource invalidSource = InputSource::create ("BadFile.exe");
        Parser parser;
        assertException (parser.parse (invalidSource), BadSourceException);
    }
};

class Parser {
public:
    void parse (const InputSource &inputSource);
    const string &parsedValue ();
};


И так дальше.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[17]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 26.09.05 13:37
Оценка:
Здравствуйте, kwas, Вы писали:

E>>А примеры вот этого влияния TDD на архитектуру можно?


K>Попробую. Подозреваю, что выйдет достаточно натянуто


Имхо, решение о том, что будет принимать конструктор Parser-а (имя файла или входной поток) должно приниматься совсем не по результатам первых unit-тестов. Но это мое сугубо субъективное мнение.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[18]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 26.09.05 14:16
Оценка:
Здравствуйте, eao197, Вы писали:

E>Имхо, решение о том, что будет принимать конструктор Parser-а (имя файла или входной поток) должно приниматься совсем не по результатам первых unit-тестов. Но это мое сугубо субъективное мнение.


Мнение, с которым я абсолютно согласен. Но это тривиальный пример, с которым все и так было более-менее понятно. В случае же с новой, неизведанной логикой, все может быть совсем по другому — опыта в данной области нет, совершенно непонятно с чего начинать. Это во-первых. Во-вторых, здесь получается некоторый снежный ком — однажды попробовав ТДД и получив приемлемые результаты, программист в следующем похожем проекте уже начнет учитывать эти результаты, что приведет к меньшему количеству рефакторингов и сокращении времени разработки. Что-то вроде: "Чем чаще применяется TDD, тем реже оно становится действительно нужным"
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[8]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 02.11.05 16:45
Оценка:
Здравствуйте, eao197, Вы писали:

E>А вот я как раз часто не понимаю... Имхо, unit-тесты хороши, когда есть какая-то библиотечка (той же сериализации, скажем) и нужно протестировать ее методы. Ну там сериализовать то-то, десериализовать, сравнить. Попробовать десериализовать то-то, посмотреть, что получилось. Это понятно. Это unit-тестами покрывается. Ну а дальше, когда библиотеки нужно в программу объединять? Вот есть у меня какой-нибудь агентик, который в каком-то состоянии на какое-то определенное воздействие определенным образом прореагировать должен. Причем агентик встраивается в какой-нибудь сложный фреймворк. Да и попадает в это состояние по сложной цепочке действий. И для моделирования всего этого приходится несколько процессов стартовать, определенные команды им выдавать. Вот в таких случаях чем пользоваться?


Наткнулся на интересный подход: Mock Objects. В частности, вот эта глава Using Mock Objects из книги Pragmatic Unit Testing. Как раз позволяет покрывать unit-тестами функциональность сложных объектов. Жалко только, что в C++ это сложно себе представить.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[8]: Зачем нужен cppunit?
От: Whirlwind Россия http://whirlwind.ru
Дата: 25.01.06 12:38
Оценка:
Здравствуйте, eao197, Вы писали:
ержался

E>А вот я как раз часто не понимаю... Имхо, unit-тесты хороши, когда есть какая-то библиотечка (той же сериализации, скажем) и нужно протестировать ее методы. Ну там сериализовать то-то, десериализовать, сравнить. Попробовать десериализовать то-то, посмотреть, что получилось. Это понятно. Это unit-тестами покрывается. Ну а дальше, когда библиотеки нужно в программу объединять?


Если вы не можете написать тест к функционалу -> этот функционал вам не нужен.

PS. и как я раньше без TDD вообще программировал?!...
Re[9]: Зачем нужен cppunit?
От: kwas Россия  
Дата: 25.01.06 15:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Наткнулся на интересный подход: Mock Objects. В частности, вот эта глава Using Mock Objects из книги Pragmatic Unit Testing. Как раз позволяет покрывать unit-тестами функциональность сложных объектов.


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

E> Жалко только, что в C++ это сложно себе представить.


Так ли уж и сложно? См. здесь
Автор(ы): Stanislav Kobylansky
Дата: 14.11.2004
Cтатья рассказывает о создании mock-объектов, предназначенных для имитации различных ситуаций, трудновоспроизводимых при реальном тестировании программного обеспечения.


.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Re[9]: Зачем нужен cppunit?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 25.01.06 16:01
Оценка:
Здравствуйте, Whirlwind, Вы писали:

W>Если вы не можете написать тест к функционалу -> этот функционал вам не нужен.


Замечательно! Просто отлично! Обязательно запомню! Как же я жил без этого вообще.

Недавно обнаружил проблему, из-за которой некоторые сообщения в распределенном приложении в одном процессе обрабатывались нормально и трансформировались, а в другой передавались в неизменном виде. Хотя, если компоненты располагались в одном процессе, никаких проблем не было. И поотдельности все работало: инициирование сообщения, его перехват и транформация, доставка сообщений в другой процесс. И приложение в других конфигурациях давно уже стабильно и без ошибок работало. А вот когда компоненты организовывали эту злополучную конфигурацию, проблема проявилась. Странно, не правда ли?

В своем сообщении я говорил не о том, что что-то не подвергается тестированию. А о том, что для некоторых вещей организовать автоматизированное тестирование (как в случае unit-тестов) очень не просто. Особенно если речь идет о распределенных приложениях и о действиях, которые нужно проверить в определенных стечениях обстоятельств, под определенной нагрузкой, при соответствующем временном распределении событий и т.д.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[14]: Зачем нужен cppunit?
От: Аноним  
Дата: 01.02.06 23:27
Оценка: +1
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, kwas, Вы писали:


K>>просто сторонние библиотеки — с ними что делать?


J>Обвязывать юнит-тестами.

J>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма

А если библиотека очень сильно завязана на hardware, новый релиз выпускается 2 раза в месяц и используется из либы порядка 100-150 функций (изначально C библиотека) + событийная модель со стейт-машиной и таймерами?

В целом отдельные вещи протестировать можно, но все ведь моками не забьешь. Может кто предложит идею, потому как надоело програмить не имея возможности протестить на спецефических видах железа, которые клиенты любят для экономии покупать , а нам геморрой
Re[4]: Зачем нужен cppunit?
От: degor Россия  
Дата: 02.02.06 07:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Написал тест, поджёг, рвануло.


отличное определение рефакторинга. точно и коротко
Re[9]: Зачем нужен cppunit?
От: crazz  
Дата: 31.07.06 06:12
Оценка:
Здравствуйте, Глеб Алексеев, Вы писали:

_W>>PS. А в чем пойнт использования макроса TEST_EQUAL(a, b) вместо TEST(a==b)?

ГА>В выводе сообщения: test a==b failed, was 3, expected 0

Крута, надо попробовать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.