Юнит-тесты это куль и руль и вообще удобное средство. Но фишка в том, что обычно тест заключается в последовательном выполнении некоторого кода и контроля за тем, что получаемые результаты, состояния объектов и вылетаемые исключения соответствуют ожидаемым.
Но при разработке библиотеки есть ещё одно важное требование: чтобы некоторые конструкции не компилировались или выдавали предупреждение компиляции (защита от неправильного использования). Есть идеи как это проверить?
Язык C++, но приветствуются любые соображения.
18.02.05 12:19: Перенесено из 'Управление проектами'
Здравствуйте, tarkil, Вы писали:
T>Приветствую!
T>Юнит-тесты это куль и руль и вообще удобное средство. Но фишка в том, что обычно тест заключается в последовательном выполнении некоторого кода и контроля за тем, что получаемые результаты, состояния объектов и вылетаемые исключения соответствуют ожидаемым.
T>Но при разработке библиотеки есть ещё одно важное требование: чтобы некоторые конструкции не компилировались или выдавали предупреждение компиляции (защита от неправильного использования). Есть идеи как это проверить?
T>Язык C++, но приветствуются любые соображения.
Первое что приходит в голову — это следующее.
Unit test может генерить тестовый файл (cpp?) и запускать компиляцию файла.
Затем можно проверять, что компиляция прошла с ожидаемой тобой ошибкой компиляции.
Тестовые файлы, которые не должны компилироваться,
можно вообще подготовить заранее и расматривать их
просто как входные данные для твоих тестов.
Здравствуйте, bkat, Вы писали:
B>Первое что приходит в голову — это следующее. B>Unit test может генерить тестовый файл (cpp?) и запускать компиляцию файла. B>Затем можно проверять, что компиляция прошла с ожидаемой тобой ошибкой компиляции.
Генерить по cpp на каждый отдельный тест, запускать на компиляцию, парсить вывод компилятора и смотреть, были ли там ошибки/предупреждения? А что, вполне рабочая схема нарисоваться может. Тем более, что парсить там можно простыми рег. выражениями.
B>Тестовые файлы, которые не должны компилироваться, B>можно вообще подготовить заранее и расматривать их B>просто как входные данные для твоих тестов.
Ну да, можно. Только много их получится очень, лучше генерить автоматически. Написать
TEST_COMPILE_ERROR(
CVerySmartPtr p = &l;
)
TEST_COMPILE_WARNING(
CVerySmartPtr p = new Obj;
)
И по каждому из макросов сгеренить cpp, закомпилировать, отловить...
Здравствуйте, tarkil, Вы писали:
T>Здравствуйте, bkat, Вы писали:
B>>Первое что приходит в голову — это следующее. B>>Unit test может генерить тестовый файл (cpp?) и запускать компиляцию файла. B>>Затем можно проверять, что компиляция прошла с ожидаемой тобой ошибкой компиляции.
T>Генерить по cpp на каждый отдельный тест, запускать на компиляцию, парсить вывод компилятора и смотреть, были ли там ошибки/предупреждения? А что, вполне рабочая схема нарисоваться может. Тем более, что парсить там можно простыми рег. выражениями.
Ага, именно это я и имел ввиду.
B>>Тестовые файлы, которые не должны компилироваться, B>>можно вообще подготовить заранее и расматривать их B>>просто как входные данные для твоих тестов.
T>Ну да, можно. Только много их получится очень, лучше генерить автоматически. Написать
Заранее подготовленные файлы удобны тем,
что тесты можно готовить не занимаясь программированием тестов.
Новый набор тестов можно подготовить без перекомпилирования юнит тестов.
Ну тут тебе виднее, как оно удобнее.
Здравствуйте, bkat, Вы писали:
B>Заранее подготовленные файлы удобны тем, B>что тесты можно готовить не занимаясь программированием тестов. B>Новый набор тестов можно подготовить без перекомпилирования юнит тестов. B>Ну тут тебе виднее, как оно удобнее.
Так перекомпилировать их так или иначе придётся — там же и проверки на результат, которые только исполнением кода можно сделать. Не, пусть одна схема будет. Проще.
Ещё милая фенька — в post-build step тестового проекта писать запуск на выполнение и выдавать ошибки юнит-теста в стиле компилятора. Тогда прогон теста является по сути компиляцией Да и на строчку можно спозиционироваться сразу.
Здравствуйте, tarkil, Вы писали:
T>Но при разработке библиотеки есть ещё одно важное требование: чтобы некоторые конструкции не компилировались или выдавали предупреждение компиляции (защита от неправильного использования). Есть идеи как это проверить?
Настоятельно рекомендую посмотреть как это тестирование реализовано в Boost::Test. Вкратце — идея построена на том, что код формируется в зависимости от define-ов
Здравствуйте, Alny, Вы писали:
A>Настоятельно рекомендую посмотреть как это тестирование реализовано в Boost::Test. Вкратце — идея построена на том, что код формируется в зависимости от define-ов
A>
A>#ifdef TEST1
A>vector<int> tst1 //компилиться
A>#endif
A>#ifdef TEST2
A>vector<int, int, int, int, int> tst1 //Не должно компилиться
A>#endif
A>
И как этим пользоваться? Задача стоит такая: запустить некий проект на компиляцию или исполнение и чтоб он выдал на stdout все найденные ошибки. Ручное включение/выключение макросов не канает, всё должно быть автоматическим.
T>И как этим пользоваться? Задача стоит такая: запустить некий проект на компиляцию или исполнение и чтоб он выдал на stdout все найденные ошибки. Ручное включение/выключение макросов не канает, всё должно быть автоматическим.
А кто говорит про ручное включение — выключение? Если используется Visual C++ — то предлагаю следующий вариант — создается солюшн, в который включается столько проектов, сколько define-ов в тестируемом коде. Этот тестируемый код строится таким образом, что любой из установленых define-ов приводит к ошибке (смотри предложеный выше вариант с TEST2).
В каждом из проектов определяем по одному define-у TEST1, TEST2 ... TESTN (Configuration->C/C++->Preprocessor->Preprocessor Difinitions).
При тестировании открываем солюшн, делаем Build->Build Solutuion, ждем, и наблюдаем заветную надпись
Это в целом то же самое, что я предлагал.
Только тесты немного иначе организованы.
Те же заранее приготовленные файлы, которые запускаем
на компиляцию и проверяем, что успех/неуспех компиляции именно тот,
какой ожидается.
Впрочем что-то принципиально иное видимо сложно предложить.
Здравствуйте, Alny, Вы писали:
A>А кто говорит про ручное включение — выключение? Если используется Visual C++ — то предлагаю следующий вариант — создается солюшн, в который включается столько проектов, сколько define-ов в тестируемом коде. Этот тестируемый код строится таким образом, что любой из установленых define-ов приводит к ошибке (смотри предложеный выше вариант с TEST2). A>В каждом из проектов определяем по одному define-у TEST1, TEST2 ... TESTN (Configuration->C/C++->Preprocessor->Preprocessor Difinitions). A>При тестировании открываем солюшн, делаем Build->Build Solutuion, ждем, и наблюдаем заветную надпись
Ну... Я запомню идею. Но смущают две вещи.
1. Оверхед при компиляции будет просто дикий — объём промежуточных файлов превысит сотню мегабайт, если проверок всего-то будет десятка два-три.
2. Сложно создавать новые проверки. Кроме того, что напиши саму проверку, так ещё и проект под неё сделай (или отдельную конфигурацию в существующем проекте... впрочем фичи build all configurations, кажется, нету).
Я всё ж попробую с автогенерацией кода и ручным запуском компилятора что-нибудь нарисовать.