Хочу кратко поделится опытом использования этих самых средств.
О себе
Так получилось, что я нарабатывал свой опыт в программировании встраиваемых систем через тесты (Unit, Integration, System, Stress). За три года мне посчастливилось пройти путь от Junior’a и написания тестов, покрывающих код других специалистов, до Senior’a с опытом разработки систем с использованием TDD методологии.
Обещанное
Упомянутый выше framework Unity очень прост и удобен в использовании. Но это всего лишь вершина айсберга. На странице throwtheswitch.org есть следующие инструменты.
CMock — инструмент позволяющий автоматически генерировать Си-код mock-ов для Ваших тестов. Написан на Ruby. Утверждаю, как человек, который на протяжении трех лет «генерировал» mock-и руками — это просто подарок для Си-разработчика. Но использовать его автономно без следующего инструмента, на мой взгляд, не рационально.
Ceedling — это целая билд-система, как утверждают сами авторы. Но по сути — это все, что Вам нужно для работы. Данный пакет содержит в себе все необходимое: Unity («тест-раннеры» и «чекалки» значений), CMock (генератор моков) и поддержку командной строки через ruby make.
Other — под этим странным заголовком находится очень, полезный, на мой взгляд инструмент — CException. Невероятно маленькая библиотека для Си позволяющая получить некое подобие исключений. Но дезинформировать не буду. В проектах использовать не довелось.
Единственное, что оставляет желать лучшего в этом многообразии прекрасных вещей, так это tutorial. Его, как бы, и нет. Все понятно, но с чего новичку начать — большой вопрос. Попробую исправить ситуацию.
Прежде всего, Ceedling должен быть корректно установлен и проверен на работоспособность как указано тут.
После установки создаем папку и тестовое окружение проекта командой:
ceedling new MyNewProject
В результате будет создана папка MyNewProject внутри которой будут сгенерированы следующие папки и файлы:
- build — сюда будут помещаться все артефакты при сборке и прогоне тестов
- src- это место для нашего «боевого» кода, который подлежит тестированию
- test — будут лежать все наши тесты
- vendor — собственно сам framework, с документацией и плагинами
- project.yml — конфигурационный файл тестового проекта. Позволяет делать хороший тюнинг, но это с опытом
- rakefile.rb — знатоки Ruby точно знают зачем этот файл. Я же, просто знаю что он необходим. Простите мою некомпетентность в данном вопросе
Пора писать первый тест.
Поместим в папку test файл test_calc.c следующего содержания:
#include "unity.h" #include "calc.h" void setUp(void) { } void tearDown(void) { } void test_add( void ) { int result = 0; result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( result, 4 ); }
Запускаем тест командой:
rake test:test_calc.c
Результат ожидаемый. Тест есть, кода нет. Проект не может быть собран.
Добавляем код.
В папку src помещаем два файла:
«calc.h»
#ifndef CALC_H #define CALC_H int calc_add(int a, int b); #endif
«calc.c»
#include "calc.h" int calc_add(int a, int b) { return a + b; }
Повторяем сборку и попытку прогнать тест:
rake test:test_calc.c
Если все сделано правильно, то в консоли должны быть результаты теста:
Test 'test_calc.c' ------------------ Compiling test_calc_runner.c... Compiling test_calc.c... Compiling calc.c... Compiling unity.c... Compiling cmock.c... Linking test_calc.out... Running test_calc.out... ------------------------- OVERALL UNIT TEST SUMMARY ------------------------- TESTED: 1 PASSED: 1 FAILED: 0 IGNORED: 0
Этот короткий пример показывает, что test-runner был сгенерирован и добавлен в сборку автоматически. Его код можно найти в папке build/test/runners.
Попробуем усложнить задачу и предположим, что наш «боевой» файл должен уметь считать только при определенном условии, проверка которого осуществляется в другом программном модуле (например, rules.c). Модифицируем код, для иллюстрации:
«calc.c»
#include "calc.h" #include "rules.h" int calc_add(int a, int b) { if (rules_is_addition_allowed()) { return a + b; } return 0; }
Добавим еще один файл в папку src:
«rules.h»
#ifndef RULES_H #define RULES_H int rules_is_addition_allowed(void); #endif
Попытка запустить тест будет неудачной, так как нет определения для функции rules_is_addition_allowed().
Самое время воспользоваться CMock.
Изменим тест следующим образом:
#include "unity.h" #include "calc.h" #include "mock_rules.h" void setUp(void) { } void tearDown(void) { } void test_add( void ) { int result = 0; rules_is_addition_allowed_ExpectAndReturn(1); result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( result, 4 ); } void test_add_off_nominal( void ) { int result = 0; rules_is_addition_allowed_ExpectAndReturn(0); result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( result, 0 ); }
Таким образом, мы получили автоматически сгенерированный mock одним лишь указанием "#include «mock_rules.h». Исходный код данного файла можно найти в директории build/test/mocks. Его изучение даст хорошее представление о том, каким образом можно менять поведение подменяемого модуля.
Оговорочки
1. Я использую данный framework только для тестирования кода на PC. Это диктует определенные правила к архитектуре разрабатываемого ПО. Прогонять юнит тесты на реальном железе смысла не вижу. HAL — он либо работает либо нет и тестируется мануально (мое видение ситуации);
2. Я не использую данный framework для тестирования нескольких потоков. Потокобезопастность данного инструмента мной не исследовалась;
3. Данная статья не учит как правильно писать код и/или тесты, а всего-лишь дает краткое представление об упомянутых выше инструментах разработки.
ссылка на оригинал статьи http://habrahabr.ru/post/244835/
Добавить комментарий