LINUX.ORG.RU

с++ тесты вместе с программой

 , ,


0

3

Всем привет!

Есть программа из файлов:

  • a.h,
  • a.cpp,
  • b.h,
  • b.cpp,
  • main.cpp.

Обычно, если это требуется, пишутся тесты для классов/функций в a, b. Раньше делал так: папку tests, туда:

  • a_tests.h
  • a_tests.cpp инклюдит a.cpp
  • b_tests.h
  • b_tests.cpp инклюдит b.cpp

отдельный main_tests.cpp.

В итоге папку tests можно собирать как отдельную программу и проверять, что все ОК.

Хочется таких разделений не делать и писать тесты прямо в a.cpp и b.cpp. Но при этом, как и прежде, получать в ходе сборки два исполняемых файла: один с main.cpp, а другой с main_tests.cpp.

Компилятор выкинет лишнее, при обычной сборке (с main.cpp)?

Или нужно об этом позаботиться?

Может у кого есть опыт, чтобы велосипед не изобретать?

Правильно ли я понимаю, что так можно и библиотеки тестить, а для покрытия кода тестами будет достаточно прикрутить gcov ?


#ifdef TESTING
void test_a() { }
#endif

И соотв. цели/аргументы в makefile.

arturpub ★★ ()

Хочется таких разделений не делать и писать тесты прямо в a.cpp и b.cpp

Не делай так.

tailgunner ★★★★★ ()
Ответ на: комментарий от arturpub

Да, такое понятно.

Но бывает, чтобы регистрируешь тесты как обычно, а затем если есть NO_TESTING оно все дропается?

ien ()
Ответ на: комментарий от tailgunner

Хочется таких разделений не делать и писать тесты прямо в a.cpp и b.cpp

Не делай так.

Дядя, поясни почему?

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

ien ()
Ответ на: комментарий от ien

Хочу мозгом понять, почему это грабли

Потому что это ужасный тон. Тесты и сам код - должны быть раздельными по определению.

I-Love-Microsoft ★★★★★ ()

Не надо так делать. Тесты зачастую занимают больший объём чем сам код, и не нужно захламлять последний этим счастьем. Мухи отдельно, котлеты отдельно.

slovazap ★★★★★ ()
Ответ на: комментарий от ien

Идеологически - потому что тесты должны быть отделены от кода; практически - например, Eclipse не всегда разбирается в ключах -D и ты теряешь все ништяки в телах тестов.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Идеологически - потому что тесты должны быть отделены от кода

#[test] (в расте) тебя не смущает?

DarkEld3r ★★★★★ ()
Ответ на: комментарий от tailgunner

Меня смущает смешивание кода и тестов.

Давай уточним когда начинается смешивание. Если код и тесты в одном файле - это уже оно?

DarkEld3r ★★★★★ ()
Ответ на: комментарий от tailgunner

Да.

Ну тогда в расте оно сплошь и рядом. А как бы ты предложил тестировать приватные внутренности модуля?

DarkEld3r ★★★★★ ()
Ответ на: комментарий от DarkEld3r

Ну тогда в расте оно сплошь и рядом

Раст не застрахован от плохой практики.

как бы ты предложил тестировать приватные внутренности модуля?

В Си++ выбора невелик - вариант из хедпоста или приватные хедеры.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

В Си++ выбора невелик - вариант из хедпоста или приватные хедеры.

Просто интересно как ты «идеальный вариант» представляешь. Лично мне кажется, что вариант «как в расте» вполне себе ничего для юнит-тестов.

DarkEld3r ★★★★★ ()
Ответ на: комментарий от tailgunner

с++ тесты вместе с программой (комментарий)

Это должно меня убедить?

Захламлять - это когда оно перемешано. Если вначале идёт код, а ниже тесты, то чем они мешают? И не надо костылей с include cpp изобретать. Другое дело, если модули/файлы огромные, ну так не стоит такого делать.

DarkEld3r ★★★★★ ()
Ответ на: комментарий от DarkEld3r

с++ тесты вместе с программой (комментарий)

Это должно меня убедить?

Это чтобы мне лишнего не писать.

Захламлять - это когда оно перемешано.

Захламлять - это когда объем тестов примерно такой же, как кода, или больше.

Если вначале идёт код, а ниже тесты, то чем они мешают?

Еще немного, и ты предложишь складывать весь текст программы в один файл. Ну а что, вначале один модуль, потом другой - чем они мешают?

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Захламлять - это когда объем тестов примерно такой же, как кода, или больше.

Если объем тестов больше кода - это здорово.

Еще немного, и ты предложишь складывать весь текст программы в один файл.

Часть про то, что я предпочитаю делать модули как можно меньше ты пропустил?

Ну и повторюсь - если мы тестируем интерфейс, то тесты должны быть снаружи. Если приватные детали реализации, то ничего плохого в тестах «внутри модуля» не вижу".

DarkEld3r ★★★★★ ()
Ответ на: комментарий от DarkEld3r

Часть про то, что я предпочитаю делать модули как можно меньше ты пропустил?

Не вижу такой части. Про «не стоит огромные» - вижу.

если мы тестируем интерфейс, то тесты должны быть снаружи

Странно. Почему «должны»? Ведь нет ничего плохого, когда тесты «внутри модуля». Или всё же есть?

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Не вижу такой части.

Но тенденцию к «складыванию в один файл» ты углядел.

Странно. Почему «должны»?

Потому что их можно вынести без всякого ущерба и дополнительных костылей. Но спор в таком направлении мне не интересен.

DarkEld3r ★★★★★ ()
Ответ на: комментарий от DarkEld3r

Пока ты один в проекте, можно вообще много всякого нафантазировать. Но вот новым разработчикам будет совсем не очевидно, что некоторые тесты нужно искать в некоторых .cpp-файлах. Разбиение на файлы — очень удобно в плане восприятия котлет и мух.

staseg ★★★★★ ()
Ответ на: комментарий от staseg

Но вот новым разработчикам будет совсем не очевидно

Новые разработчики почитают доку на вики или спросят, в конце концов. И они точно так же «могут не ожидать» '#include «something.cpp»'.

DarkEld3r ★★★★★ ()
Ответ на: комментарий от DarkEld3r

Новые разработчики почитают доку на вики или спросят, в конце концов.

Клевая дока, ага. У нас 100500 тестов, 100 из них в отдельных файлах, 500 вот в этом, а еще 500 вон в том. А главное — поддерживать эту доку актуальной тоже очень легко.

И они точно так же «могут не ожидать» '#include «something.cpp»'.

module.cpp, module_test.cpp — ну што тут неожиданного? Кого вообще волнует, какие там внутри include-ы?

staseg ★★★★★ ()
Последнее исправление: staseg (всего исправлений: 1)
Ответ на: комментарий от staseg

Клевая дока, ага. У нас 100500 тестов, 100 из них в отдельных файлах, 500 вот в этом, а еще 500 вон в том.

Достаточно написать: «юнит-тесты для приватных кишок в том же файле, а остальные тесты (например) в директории tests каждого модуля».

Кого вообще волнует, какие там внутри include-ы?

Ну кого-то же волнует «загромождение кода тестами».

DarkEld3r ★★★★★ ()
Последнее исправление: DarkEld3r (всего исправлений: 1)

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

Имхо, если других ограничений нет, так делать как минимум неудобно.

pon4ik ★★★★★ ()

Слегка раскрою своё предыдущее утверждение (писал с мобилы прост).

Юнит тесты без tdd (имхо конечно же) - пустая трата времени, выхлоп будет мизерный, тесты будут уродливы и код тоже не очень (по крайней мере на уровне интерфейсов).

Я, обычно, наоборот начинаю новую реализацию в файлике с тестом, а потом выношу её в отдельный модуль где то в процессе реализации (когда визуально перестаю быстро охватывать кейсы и реализацию одним взглядом или когда реализация готова).

Получается микро api, для реализации прикладной задачи. У которого уже закреплён интерфейс в тестах.

Почему плохо идти по питонячьему или растовому пути с написанием теста прямо в коде? Ответ прост, сии языки имеют поддержку такого подхода на уровне языка и/или фреймворка, а кресты - не имеют. И твой топик - есть подтверждение того факта, что отсутсвие поддержки реализациией сий процесс усложняет, тогда как тесты должны писаться максимально незатратно.

Хороший путь тут предлагает например CMake - делать из реализаций статические либы, линковать их как в исходный бинарь так и в тестовый. Если статические либы кажутся слижком тяжеловесными или не подходят ещё почему-то, то есть решение и на уровне обьектников. Но никак не #include «foo.cpp» :). Конечно же появляются так называемые приватные хидеры, но это скорее хорошо чем плохо, ибо если сегодня api приватное, это не значит, что после пары сессий рефакторинга оно не станет публичным, ну а его интерфейсы уже будут протестированны и доступны в хидерах.

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

Кстати сразу возникает вопрос - какой тестовый фреймворк используешь?

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Юнит тесты без tdd (имхо конечно же) - пустая трата времени, выхлоп будет мизерный

O_o

Ответ прост, сии языки имеют поддержку такого подхода на уровне языка и/или фреймворка, а кресты - не имеют.

Питон не имеет поддержки юнит-тестов на уровне языка, а на уровне фреймворка поддержка есть и в Си++.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Питон динамический, там что фреймворк что язык, грань тонка.

Юнит тесты без тдд максимум помогут зафиксировать баги, тестировать уже протестированную реализацию - смысл? По крайней мере на уровне кода. Проще функциональный тест написать тогда уж.

pon4ik ★★★★★ ()
Ответ на: комментарий от tailgunner

Но, зато, в питоне есть модули, в отличии от плюсов. Плюс, этими модулями можно оперировать в рантайме.

Да, и что считать языком - стандартная библиотека, часть языка или нет?

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Питон динамический, там что фреймворк что язык, грань тонка.

Не понял, причем тут динамизм. Я пользовался unittest в Python и пользуюсь Catch в Си++ - ИМХО, Catch круче.

стандартная библиотека, часть языка или нет?

Нет. Вот unittest {} в D - часть языка.

Юнит тесты без тдд максимум помогут зафиксировать баги, тестировать уже протестированную реализацию - смысл?

Протестированную кем или чем? Для тестирования реализации пишутся юнит-тесты, прямо по ходу реализации, и реализация не является законченной, пока не работают юнит-тесты.

Впрочем, делать юнит-тесты протестированной реализацию тоже имеет смысл - например, перед рефакторингом.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Динамизм питона при том - что в рантайме модуль может проверить тест ли он. В плюсах нет модулей в таком понимании.

Для тестирования реализации пишутся юнит-тесты, прямо по ходу реализации, и реализация не является законченной, пока не работают юнит-тесты.

Это всякие маркетологи и прозвали tdd :D

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

pon4ik ★★★★★ ()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от tailgunner

offtop

А что использовал вместе с Catch, для моков?

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Динамизм питона при том - что можно в рантайме модуль может проверить тест ли он.

Наверное. Я, правда, не понимаю, какие принципиальные преимущества это дает, но ТНБ с ним.

Для тестирования реализации пишутся юнит-тесты, прямо по ходу реализации, и реализация не является законченной, пока не работают юнит-тесты.

Это всякие маркетологи и прозвали tdd :D

Чувствую себя месье Журденом.

tailgunner ★★★★★ ()
Ответ на: offtop от pon4ik

Пока ничего. Аноним с ЛОР советовал Hippomocks, но у меня пока руки не дошли.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Как то мёртво он выглядит...

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

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

Последний коммит 17 ноября - не похоже, чтобы автор забил.

tailgunner ★★★★★ ()
Ответ на: комментарий от DarkEld3r

А как бы ты предложил тестировать приватные внутренности модуля?

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

hobbit ★★★★★ ()
Ответ на: комментарий от staseg

Новых разработчиков стоит иногда посвящать в такие незначительные детали, как структура проекта.

arturpub ★★ ()
Ответ на: комментарий от tailgunner

Ты ещё предложи документацию в расте отдельно выносить.

anonymous ()
Ответ на: комментарий от hobbit

Если модуль требует отдельного теста для своих приватных внутренностей

Дык, юнит-тесты предполагают тестирование отдельных функций/методов? Ну и в ряде языков есть поддержка тестов как раз с доступом к внутренностям модуля, значит это является востребованным и не всеми рассматривается как плохой дизайн и «захламление».

В плюсах я так не делаю, если что.

DarkEld3r ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.