Comments 33
Все это к сожалению далеко от практической плоскости разработки для embedded.
Во первых вы не указали, что для того, чтобы писать тесты, в первую очередь нужны спецификации — т.е. формализованные требования, которые бы указывали, что ваша железяка вместе с ПО должна делать. Без них вы можете написать хоть тысячу тестов, которые не будут тестировать то, что вам надо.
Во-вторых проблема эмбеддед разработки в том, что требования выше обычно предъявляются к комбинации ПО+железо, а не к чистому ПО. Потому что сделать спецификацию на ПО многим откровенно лень, да и тестировать нужно все вместе — никому не нужно встраиваемое ПО, которое прекрасно работает в симуляторе, но не работает на реальном железе.
Ну и в третьих, если учитывать первые два пункта, разработка тестов для такого рода задач становится уже не тривиальной задачей написания кода за 5 минут, а вполне себе отдельным проектом, в котором код сочетается с тестовыми стендами и эмуляторами, которые имитируют внешние воздействия и считывают реакцию системы. И часто процесс разработки такого TDD стоит гораздо дороже разработки самого встраиваемого ПО и поэтому
в четвертых — обычный стол, пара кнопочек, светодиоды, мультиметр и осциллограф — наши друзья. Иногда ручной тестовый стенд. А автоматические тесты заменяем ручками :-) И это не потому, что так хорошо тестировать встраиваемое ПО, а потому, что по другому гораздо сложнее.
В корне с вами не согласен.
Скорее всего вы путаете системное тестирование (черный ящик) и юнит тестирование (белый ящик) .
т.е. формализованные требования, которые бы указывали, что ваша железяка вместе с ПО должна делать
Для юнит тестирование не нужны требования, нужна детальная архитектура. Т.е. описание (спецификация) функции, метода, класса. Вам все равно придется это делать. К вашей конкретной функции никаких требований никто не предъявляет, кроме вас самого… Таким образом, вы вначале сами описываете, что должна делать функция, входные, выходные параметры и можете либо сразу реализовать это, а потом покрыть функцию юнит тестами, либо вначале сделать юнит тесты, а потом сделать реализацию под эти тесты.
Во-вторых проблема эмбеддед разработки в том, что требования выше обычно предъявляются к комбинации ПО+железо, а не к чистому ПО.
Опять таки, скорее всего путаница между тестированием черного ящика, и белого. Ваш код функции по сути и есть спецификация функции и чтобы убедиться, что она делает, то, что вы задумали, вы и пишите юнит тест.
А вот как работает софт и железо вместе — уже пусть системные тесты проверяют, но если у вас нет требований, то тогда и проверять то нечего :)
Ну и в третьих, если учитывать первые два пункта, разработка тестов для такого рода задач становится уже не тривиальной задачей написания кода за 5 минут
Она точно такая же задача, как и написание кода. Вы можете точно также писать функцию, потом прошивать микроконтроллер, потом смотреть осцилографом, потом понять, что функция работает не так, снова менять функцию...(по сути будете делать ручной юнит тест). Вопрос зачем и сколько вы на это потратите время? Если все можно сделать юнит тестом?
Да еще и если потом кто-то поменяет вашу функцию, и она перестанет работать, так как вы задумали, вы даже об этом и не узнаете… Вообще замечательно.
С последним вашим абзацем почти согласен, юнит тесты используются, чтобы автоматизировать и проверить, что ничего с вашей функцией не случилось, что она работает так как вы задумали.
Или каждый раз прошивать и проверять, или просто запустить юнит тесты. Профит со всех сторон, экономия времени, ресурсов и затрат.
Да, я говорю о системном тестировании, поскольку слово юнит-тестирование в статье не встречается ни разу, а применимость TDD обсуждается в контексте подхода к разработке встраиваемого ПО в целом (типа встраиваемого ПО блока управления тормозной системой, а не одной из функций этого ПО)
То есть вы хотите сказать, что автор забыл упомянуть этот момент?
Во-вторых и в третьих, Обычно для теста разработанной системы «ПО + железо» используют системное тестирование. Действительно, это отдельная задача. В этой статье говорится о методологии TDD с целью разработки бизнес-логики, которая будет работать в конечном продукте. А тестирование системы «ПО + железо» в реальных условиях будем проводить в третьей части цикла статей вручную.
В качестве IDE – Visual Studio, потому что нам нравится ее внешний вид, удобство отладки и рефакторинга кода. Данная IDE также подходит для написания кода на «чистом» C.
А как у нее с С99? Неужели добавили поддержку?
Вообще CppUTest можно использовать и с родным компилятором для микроконтроллера, а его можно подключить в IDE Visual Studio.
Но тогда их надо запускать или в симуляторе родной архитектуры МК, либо на тестовой плате и получать результаты тестов на комп каким-то образом.
Вообще, я может невнимательно читал, но в статье я не нашел, под какую платформу происходит компиляция и как происходит запуск.
Мне показалось, что тесты компилируются под х86 и запускаются там же...
Все верно, по моему опыту 90-95% юнит тестов нормально на симуляторе можно проверить. На IAR все это делается через C-SPY.
По поводу х86, да тоже не совсем понял из статьи на какой платформе запускаются тесты, но ничего не ограничивает (кроме ресурсов (например, ОЗУ) микроконтроллера) запустить их на целевой платформе, в данном случае ARM CortexM.
Мой опыт тестов в симуляторе Keil говорит примерно то же самое; единственное что тесты иногда довольно долго выполняются.
У меня есть старая статья (которая, правда, не совсем про это), сейчас я пользуюсь другим фреймворком; тоже самодельным, но уже на С++, регистрировать тесты гораздо удобнее и boost не нужен.
Если коротко, то все сводится к запуску в симуляторе, с перенаправлением вывода printf в отладочное окно.
Как-то сходу ничего больше на нашел. Но если хотите, могу статью написать :)
C-SPY чтобы запустить юнит тесты на целевой платформе, либо в симуляторе, либо прямо на плате. Не совсем понял, что вы имели ввиду под задавать assertы. Но результаты с тестов выдаются через Terminal I/O.
Подробнее можете здесь прочитать:
https://cpputest.github.io/stories.html
void Configurator_Destroy(Configurator * self)
{
if (self == NULL) // Зачем?
{
return;
}
free(self);
self = NULL; // Зачем?
}
Согласно спецификации free() сама должна проводить проверку параметра на неравенство нулю. Обнуление self перед выходом так же не имеет никакого смысла.Для чего это все на микроконтроллерах, если есть отладка?
Как решается проблема взаимодействия с реальным железом?
Для ускорения разработки. При правильном подходе к юнит тестам, отладку можно вообще не делать.
Отладка может делаться в двух случаях:
- вы не знаете, как что должно вообще работать (например, не прочитали документацию, или первый раз знакомитесь с этим модулем и не до конца знаете как его настраивать, либо скудная документация (как например, на Миландр) с кучей ошибок) и тогда вы делаете отладку, но только для того, чтобы понять, как вообще это все работает — это считайте не разработка — это исследование.
- отлавливание багов, вынесенных с системного тестирования
В нормальной разработке, когда все в мануалах на микроконтроллер прописано, достаточно сделать юнит тесты один раз, чтобы быть увереным, что ваш модуль работает как надо и никто ничего не поменял.
К примеру, настраиваете вы модуль SPI на нужные вам настройки (полярность, фазу и так далее) в методе Config()
. Вы можете сделать юнит тест, который проверит, что после вызова этого метода установлены нужные биты регистров SPI. И запускать этот тест каждый раз при сборке. Если какой-то программист Снежинка взял и поменял фазу (считывание данных с переднего на задний фронт), у вас и дальше все может продолжить работать, в том числе и под отладкой, и вы не заметите подвоха, а вот пользователь вашего устройства, работающий на -50С уже заметить, так как до этого SPI работал на пределе, небольшой сдвиг от температуры и вуаля, ничего не работает
А вот юнит тест это бы увидел — настройка в регистре не та… — тест провалился.
При этом возникает резонный вопрос, если вы настройку сделали в полном соответствии с документацией на микросхему, зачем вообще это отлаживать?
А проблему взаимодействия с «железом» обсудим в следующей части этого цикла статей.
TDD концепция применима только к тривиальным приложениям. Потому что в более менее средней по сложности системе изначально не понятно как все будет выглядеть в конце, и соответсвенно о каких тестах можно говорить. Сначала пишется прототип, потом он дорабатывается и затем делают рефакторинг и покрывают тестами.
Это не только в embedded, а вообще везде в IT. Мне интересно те кто говорит про TDD вообще хоть что то сами писали?
p.s.
Тем более мне непонятно как TDD совмещается с Agile, TDD это более waterfall model — это реальный парадокс.
P.S. Тем более не понятно, как TDD совмещается с waterfall моделью, когда как эта техника требует проведение маленьких итераций разработки.
Мне интересно, те, кто говорит про неприменимость TDD вообще когда-нибудь пробовали эту методологию или хоть пробовали писать что-то действительно сложное, когда отладка превращается в ад и сжирает намного больше времени, чем разработка?
Пробовали, и тут получается палка о двух концах — если упереться в TDD, то на написание тестов и их поддержку может тратиться больше ресурсов, чем на саму разработку, но зато имеем сокращенное время отладки. И наоборот.
То есть в одних случаях может быть выгодно первое, а в других второе. Например, если вносить изменения в прошивки после выпуска не требуется или требуется, но очень редко, то держать наготове TDD фреймворк имеет мало смысла — он просто устареет к тому времени, когда понадобится в следующий раз. А вот если у вас, как у Google, прошивки выходят каждый месяц, то да — есть смысл.
Ну так никто не говорит о TDD как о панацее! Вы совершенно правы, иногда это оправдано, иногда — нет. Но возможно)
Как уже сказали, TDD и в самом деле не панацея. Но я считаю что это важный и нужный инструмент, приносящий пользу.
TDD для микроконтроллеров. Часть 1: Первый полет