Здравствуйте, eao197, Вы писали:
E>Все вышесказанное, естественно, ИМХО. Но интересно, что об этом другие участники форума думают.
Сразу оговорюсь, что масштабы проектов, над которыми я работал, не позволяют мне авторитетно, тэкскть, высказаться.
Unit-test'ы — это самый нижний уровень. Есть еще functional, performance и прочие тесты. Например, в этом году пришлось писать XMLDA-сервис, и наличие готового test-suite — это была просто манна небесная.
Но даже сами по себе юнит-тесты — это гигантский прорыв. Почему? Из-за элементарной комбинаторики. Если в компоненте A 4 метода, нужно, грубо говоря, по одному тесту на каждый из них (в каждом тесте проверяются граничные и нормальный случаи). А вот если для этого компонента юнит-теста нет, то нужно тестировать все функции, в которых этот компонент используется, и ошибка в одном из граничных случаев одного из методов A будет в клиенте вылезать только в 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)
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Но даже сами по себе юнит-тесты — это гигантский прорыв.
Собственно, с этим я и не спорил. Просто мне интересно, а есть что-нибудь аналогичное, автоматизированное, но для более высокого уровня. Может кто чем-то пользуется. Интересны впечатления.
ГА>Прочувствовал это на последнем проекте, у нас, к сожалению, юнит-тесты не приняты, а также полно велосипедов (своя строка, свой вектор, свой ХМЛ-парсер , свои обертки над потоками/мутексами, своя реализация паттерна Обсервер, и т.д.). И сколько раз приходилось убивать часы в отладчике (расставлять трассировку по всему проекту), чтобы найти очередной глюк в строке . Мораль — если базовая библиотека хорошо покрыта элементарными юнит-тестами, остальную логику, в том числе ГУИ, можно с высокой степенью уверенности тестировать ручками и спать спокойно по ночам.
Так если ты считаешь, что unit-тесты должны быть, то настаивай на их внедрении.
А что касается велосипедов... Будучи сам патологическим велосипедоманом, хочу сказать, что есть порода такая -- велосипедостроители. Попадется вам вменяемый велосипедостроитель, которого получится в рамках удерживать -- будут у вас полезные и отлаженные велосипеды. А нет -- тогда действительно пиши пропало
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Так если ты считаешь, что unit-тесты должны быть, то настаивай на их внедрении. E>А что касается велосипедов... Будучи сам патологическим велосипедоманом, хочу сказать, что есть порода такая -- велосипедостроители. Попадется вам вменяемый велосипедостроитель, которого получится в рамках удерживать -- будут у вас полезные и отлаженные велосипеды. А нет -- тогда действительно пиши пропало
Как проверить вменяемость велосипедостроителя? Вижу два пути:
а) наличие тестов,
б) вставка велосипеда в проект.
Тут тоже есть свои подвохи:
а) наличие тестов не гарантирует вменяемости, так как сами тесты могут оказаться невменяемыми — вырожденный случай, из категории "тупица",
б) тестов нет, велосипед пишется тут же, багов нет, все отлично — вырожденный случай, из категории "гений"
б) наличие тестов в наличии, но баги есть, так как тестами покрыли не все случаи — добавляем тест, "ночью спим спокойно" (с),
в) если велосипед без тестов работает, ничего не падает, то это также не гарантирует вменяемости, особенно если велосипедостроитель пришел с уже готовыми велосипедами. Потому как кто его знает, сколько бессонных ночей он провел в отладчике, вылизывая свой велосипед. И разрешив ему писать новые велосипеды, можно сильно ухудшить ситуацию.
If a shark stops swimming, it will die. Don't stop swimming, Mr. Mulder.
Every epic equalizer is iso (c)
Здравствуйте, kwas, Вы писали:
K>Как проверить вменяемость велосипедостроителя? Вижу два пути: K>а) наличие тестов, K>б) вставка велосипеда в проект.
Вы смотрите на ситуацию со стороны начальника, т.е. велосипедостроитель — обязательно подчиненный. Я работаю в команде из 3х человек, выраженной иерархии у нас нет, но велосипедостроитель, не желающий слышать о юнит-тестах — старший товарищ, типа где-то даже начальник. Да, и такое бывает .
Поэтому я, когда пишу что-то явно библиотечной направленности (типа bicycle_lib::tree<T>), то добровольно рисую юнит-тест, если в нашей общей bicycle_lib обнаружился глюк, опять же рисую тестик для обнаружения этого глюка, но покрытие тестов очень низкое. Так вот, даже в этой ситуации юнит-тесты здорово помогают, при том, что никакого TDD и в помине нет.
Здравствуйте, 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++.
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Вы смотрите на ситуацию со стороны начальника, т.е. велосипедостроитель — обязательно подчиненный. Я работаю в команде из 3х человек, выраженной иерархии у нас нет, но велосипедостроитель, не желающий слышать о юнит-тестах — старший товарищ, типа где-то даже начальник. Да, и такое бывает .
Знаю, сам с таким постоянно сталкиваюсь, и постоянно пытаюсь сподвигнуть на boost (если, конечно, есть аналоги), кивая в первую очередь на оттестированность (как юнит-тестами, так и пользователями в проектах). Получается с переменным успехом
ГА>Поэтому я, когда пишу что-то явно библиотечной направленности (типа bicycle_lib::tree<T>), то добровольно рисую юнит-тест, если в нашей общей bicycle_lib обнаружился глюк, опять же рисую тестик для обнаружения этого глюка, но покрытие тестов очень низкое. Так вот, даже в этой ситуации юнит-тесты здорово помогают, при том, что никакого TDD и в помине нет.
С интерфейсом tree, на мой взгляд, довольно трудно напортачить, тем более если вы изобретатель со стажем Благо есть немало примеров этих самых интерфейсов. Согласен, для вещей, интерфейс которых предсказуем, если не сказать стандартен, ваш подход работает на 100%. А вот новая бизнес-логика, для которой библиотек нет в помине, или просто сторонние библиотеки — с ними что делать? Взять вот тот же пример eao197
:
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)
Здравствуйте, kwas, Вы писали:
K>просто сторонние библиотеки — с ними что делать?
Обвязывать юнит-тестами.
Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма
Здравствуйте, jazzer, Вы писали:
K>>просто сторонние библиотеки — с ними что делать?
J>Обвязывать юнит-тестами. J>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма
А как, интересно, обвязать unit-тестами, например, iconv или libxml2?
Может лучше, если убеждаешься, что баг именно в библиотеке, написать один единственный тест, которые показывает наличие именно этого бага. И новые версии библиотек проверять этим тестом. И только когда появится новый баг, то написать для него отдельный тест.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
<...>
K>Более интересны как раз граничные случаи: что будет, если парсеру подсунуть .cpp файл вместо сериализованного файла? Что будет, если сериализованный файл попортится где-нибудь в серединке? Что будет, если попытаться десериализовать объект одного типа в объект другого типа, несовместимого с исходным? Вполне возможно, что eao197 о его тестировании таких случаев просто не счел нужным упомянуть, но я как раз их считаю более важными, и именно из бережного к ним отношения вырастает красивая архитектура.
Эти случаи интересны, если библиотека претендует на выживаемость в таких условиях. Но, нужно сказать, что подобная надежность покупается избыточностью (либо увеличением сериализованного образа, либо времени сериализации/десериализации, либо и того, и другого). Что не для всех задач приемлимо. Хрустальная ваза должна быть красивой и не протекать. А выдерживать удары молотком или падения на пол вовсе не обязана.
Кроме того, имхо, архитектура к покрытию тестами не имеет отношения вообще.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>А как, интересно, обвязать unit-тестами, например, iconv или libxml2?
E>Может лучше, если убеждаешься, что баг именно в библиотеке, написать один единственный тест, которые показывает наличие именно этого бага. И новые версии библиотек проверять этим тестом. И только когда появится новый баг, то написать для него отдельный тест.
Ну ты же не всем iconv'ом пользуешься, а некоторыми юз-кейсами. Обвязываешь их.
То есть, попросту, всё то, что ты обвязал бы ASSERT'ами внутри программы вокруг вызова iconv, — выносишь в тест. (Ассерты тоже можно оставить).
Здравствуйте, Кодт, Вы писали:
К>Ну ты же не всем iconv'ом пользуешься, а некоторыми юз-кейсами. Обвязываешь их. К>То есть, попросту, всё то, что ты обвязал бы ASSERT'ами внутри программы вокруг вызова iconv, — выносишь в тест. (Ассерты тоже можно оставить).
Чесно говоря, так и не приобрел привычки ASSERT-ами пользоваться. Практически их не пишу. Да и если бы писал, то толку в них особого не было бы -- компилюсь обычно сразу в release-режиме, чтобы затем не бодаться с ситуациями, когда в debug работает, а в release -- нет.
Но все равно, кажется мне это параноидальным. Ведь я выбираю какую-то библиотеку для себя. Естественно, обращаю внимание на то, насколько широко она используется. Беру только stable-версии. Желательно постарше. Т.е. я расчитываю на то, что автор библиотеки и ее пользователи потратили достаточно времени, чтобы погонять ее.
Если же я сам начинаю делать тестовые обвязки вокруг библиотеки (даже не встретив реального бага), то я делаю работу, которую, с большой вероятностью, можно считать бесполезной. А ведь я мог бы потратить тоже самое время на свой прикладной код, на написание теста для него, к примеру.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, 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)
Здравствуйте, kwas, Вы писали:
K>>>просто сторонние библиотеки — с ними что делать? J>>Обвязывать юнит-тестами. J>>Потому что потом выйдет новая версия этой же библиотеки, и в эту версию азработчики посадят нехороший баг, а падать будет твоя программма
K>Резонно. С ненужностью я погорячился. То есть, тривиальные операции тоже проверяем. И граничные случаи. И заведомо неправильные данные. И так для каждой библиотеки. Why me? Да понятно, почему — потому что мне это надо.
Интересно, а strcat так же я должен у себя протестировать? Ведь использую.
Или Boost.Test должен я предварительно протестировать, прежде чем на его основе свои тесты писать?
Или Boost.MultiIndex или Boost.FileSystem?
Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, 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)
Здравствуйте, kwas, Вы писали:
E>>Хрустальная ваза должна быть красивой и не протекать. А выдерживать удары молотком или падения на пол вовсе не обязана.
K>Но и исчезать беззвучно, аннигилируя все в округе в радиусе 1 км, ваза тоже не должна, не правда ли? Даже если ее бъют молотком. Вот и проверяется то, что ваза просто разобъется на осколки, разлетевшиеся на 2 метра во все стороны.
Имхо, если что-то пошло не так уж лучше как можно скорее грохнуться, да еще с максимально возможным шумом. Но не факт, что этот шум можно поднять. Последствия от брошенного, но не пойманного std::exception или от вызова abort() могут быть самыми разными. Причем, что важно, библиотека может позволить себе либо выбросить исключение, либо вызвать abort() в зависимости о того, насколько она оценивает тяжесть своего положения. Подобные вопросы поднимались в теме Checked exceptions... зло или добро?
.
E>>Кроме того, имхо, архитектура к покрытию тестами не имеет отношения вообще.
K>При тестировании постфактум — естественно. При TDD — имхо, имеет. TDD заставляет (меня, по крайней мере, точно) задумываться о граничных и неправильных входных данных. Это очень часто влияет на интерфейс тестируемого класса/метода, и, следовательно на архитектуру.
А примеры вот этого влияния TDD на архитектуру можно? А то я, например, отталкиваюсь от удобства использования инструмента в повседневной работе, а проблемы тестирования на втором плане.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, 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)
Здравствуйте, eao197, Вы писали:
E>Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?
А нигде. Помните ошибку в FPU первого Пня? Вот тоже все были уверены, что 2.0 * 2.0 = 4.0
Во-первых, есть людская молва и слава. Скажем, какие-то авторы славятся своей криворукостью (например, ваши партнёры из Индии или из соседнего кабинета).
Во-вторых, юнит-тесты — это способ освоения библиотеки. Любую документацию, по законам Мерфи, можно прочесть неправильно.
Вот появилась в вашем распоряжении сторонняя библиотека. Первым делом практикуешься с ней. Пишешь тест, от которого ждёшь такое-то поведение. Написал, запустил, обломился. Полез в документацию. Ага, это недопонимание... Переписал тест. Запустил, обломился. Ага, это багрепорт. И так далее.
Всё понял, всё работает... а тесты выкидывать не надо: пусть лежат до следующей версии.
Здравствуйте, Кодт, Вы писали:
E>>Где критерий, который позволяет сказать: вот этой библиотеке я доверяю и тестировать ее дополнительно не буду, а вот этой нет доверия, и я ее тестами покрою?
К>А нигде. Помните ошибку в FPU первого Пня? Вот тоже все были уверены, что 2.0 * 2.0 = 4.0
Так в том-то и дело. Есть ошибки, от которых бесполезно пытаться защититься. Это как падающие балконы -- в подавляющем большинстве они не падают, но прецеденты бывают
К>Во-вторых, юнит-тесты — это способ освоения библиотеки. Любую документацию, по законам Мерфи, можно прочесть неправильно. К>Вот появилась в вашем распоряжении сторонняя библиотека. Первым делом практикуешься с ней. Пишешь тест, от которого ждёшь такое-то поведение. Написал, запустил, обломился. Полез в документацию. Ага, это недопонимание... Переписал тест. Запустил, обломился. Ага, это багрепорт. И так далее. К>Всё понял, всё работает... а тесты выкидывать не надо: пусть лежат до следующей версии.
Честно говоря, не нравится мне знакомиться с библиотекой по ее unit-тестам. Имхо, у библиотеки должны быть тесты (tests, unittests) и примеры (sample, examples). Первые предназначены для разработчиков библиотеки. Там может и, в принципе, должно быть погружение в такие дебри, которые обычному пользователю библиотеки и знать-то не обязательно. Как правильно сказал kwas -- проверки на всякие граничные условия и экстремальные режимы
. А вот примеры должны показывать как нужно пользоваться библиотекой в штатном режиме, без экстрима.
Вот при наличии примеров изучение новой библиотеки для меня идет по другому, чем ты описал, сценарию:
-- читаю документацию (да, в отличии от настоящих программистов, стараюсь читать документацию до написания кода );
-- пытаюсь разобраться в примере;
-- при отсутствии понимания возвращаюсь к чтению документации;
-- начинаю курочить примеры подгоняя их под то, что нужно мне в моей задаче;
-- при достижении полного понимания того, что мне нужно, пишу чистовой код в своем проекте.
А раскуроченные примеры из состава библиотеки выглядят, обычно, как совершенно нечитабельные черновики. И пылятся себе где-нибудь в дебрях ~/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++.
Здравствуйте, eao197, Вы писали:
E>Вот, в качестве иллюстрации того, почему мне не нравится изучать библиотеки по unit-тестам, пример теста ACE_Date_Time из ACE. Для того, чтобы понять, как этим классом пользоваться в примере (sample) достаточно было бы написать всего-то выделенный жирным фрагмент. А все остальное -- это оверхед, который важен при тестировании, но совершенно не нужен при изучении библиотеки.
Тут, имхо, просто хреновый код юнит-тестирования. Хороший код будет выглядеть декларативно — как описание, какие контракты поддерживает библиотека. Грубо говоря (недо-код бустоподобный):
И в таком виде этот код отнюд не бесполезен. Он отвечает на не совсем тривиальные вопросы типа "месяца, дни, часы считаем от 0 или от 1?", "каковы допустимые значени лет?" и т.п.
А так же утверждает, что в static_dt хранятся значения, совпадающие с dt, полученным при помощи конструктора без параметров (текущая дата, видимо?)