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
Оценка:
Здравствуйте, Кодт, Вы писали:

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


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