Не запускать юнит-тесты на устройстве Arduino или эмуляторе
Дело против микроконтроллера Устройства / Эмулятор / Сим-тесты
Существует много дискуссий о том, что означает модульный тест , а я нет
действительно пытаюсь спорить об этом здесь. Этот пост не
говорю вам избегать всех практических испытаний вашей конечной цели
аппаратное обеспечение. Я пытаюсь сделать вывод об оптимизации вашего
цикл обратной связи разработки, устраняя целевое оборудование от
ваши самые обыденные и частые тесты. Предполагаемые единицы измерения
быть намного меньше, чем весь проект.
Целью модульного тестирования является проверка качества вашего собственного кода. Модульные тесты, как правило, никогда не должны проверять функциональность факторов, находящихся вне вашего контроля.
Подумайте об этом так: даже если бы вы тестировали функциональность библиотеки Arduino, аппаратного обеспечения микроконтроллера или эмулятора, абсолютно невозможно , чтобы результаты такого теста могли рассказать вам о качестве вашей собственной работы. Следовательно, гораздо полезнее и эффективнее писать модульные тесты, которые не запускаются на целевом устройстве (или эмуляторе).
Частое тестирование на целевом оборудовании имеет мучительно медленный цикл:
- Настройте свой код
- Компиляция и загрузка на устройство Arduino
- Наблюдайте за поведением и угадывайте, делает ли ваш код то, что вы ожидаете
- Повторите
Шаг 3 особенно неприятен, если вы ожидаете получать диагностические сообщения через последовательный порт, но сам ваш проект должен использовать единственный аппаратный последовательный порт вашего Arduino. Если вы думали, что библиотека SoftwareSerial может помочь, вы должны знать, что это может нарушить любую функциональность, которая требует точной синхронизации, например, генерация других сигналов одновременно. Эта проблема случилась со мной.
Опять же, если вы должны были протестировать свой эскиз с помощью эмулятора, и ваши критичные по времени процедуры выполнялись идеально до тех пор, пока вы не загрузили его в реальный Arduino, то единственный урок, который вы собираетесь извлечь, - это то, что эмулятор имеет недостатки - и Знание этого все еще не показывает ничего о качестве вашей собственной работы.
Если глупо тестировать на устройстве или эмуляторе, что должен сделать?
Возможно, вы используете компьютер для работы над проектом Arduino. Этот компьютер на несколько порядков быстрее, чем микроконтроллер. Напишите тесты для сборки и запустите на своем компьютере .
Помните, что поведение библиотеки Arduino и микроконтроллера должно быть предполагаемым либо правильным, либо, по крайней мере, последовательно неверным .
Когда ваши тесты дают результаты, противоречащие вашим ожиданиям, то, скорее всего, у вас есть недостаток в тестируемом коде. Если результаты теста соответствуют вашим ожиданиям, но программа не работает должным образом, когда вы загружаете его в Arduino, то вы знаете, что ваши тесты основывались на неверных предположениях, и вы, вероятно, имеете ошибочный тест. В любом случае вам будет дано реальное представление о том, какими должны быть ваши следующие изменения кода. Качество вашей обратной связи улучшено с " что-то сломано" до "этот определенный код сломан" .
Как создавать и запускать тесты на вашем компьютере
Первое, что вам нужно сделать, это определить ваши цели тестирования . Подумайте, какие части вашего собственного кода вы хотите протестировать, а затем убедитесь, что ваша программа построена таким образом, что вы можете выделить отдельные части для тестирования.
Если части, которые вы хотите проверить, вызывают какие-либо функции Arduino, вам нужно будет предоставить макетные замены в вашей тестовой программе. Это гораздо меньше работы, чем кажется. Ваши макеты не должны ничего делать, кроме обеспечения предсказуемого ввода и вывода для ваших тестов.
Любой ваш собственный код, который вы собираетесь тестировать, должен существовать в исходных файлах, кроме эскиза .pde. Не волнуйтесь, ваш эскиз все равно будет компилироваться даже с некоторым исходным кодом за пределами эскиза. Когда вы действительно приступите к этому, в файле эскиза должно быть определено немного больше, чем обычная точка входа вашей программы.
Осталось только написать реальные тесты, а затем скомпилировать их, используя ваш любимый компилятор C ++! Это, вероятно, лучше всего проиллюстрировано на примере реального мира.
Фактический рабочий пример
Один из моих любимых проектов, найденных здесь имеет несколько простых тестов, которые выполняются на ПК. Для этого ответа я просто расскажу, как я смоделировал некоторые функции библиотеки Arduino и тесты, которые я написал, чтобы проверить эти макеты. Это не противоречит тому, что я говорил ранее о не тестировании чужого кода, потому что я был тем, кто написал макеты. Я хотел быть уверен, что мои макеты были правильными.
Источник mock_arduino.cpp, который содержит код, дублирующий некоторые функции поддержки, предоставляемые библиотекой Arduino:
#include <sys/timeb.h>
#include "mock_arduino.h"
timeb t_start;
unsigned long millis() {
timeb t_now;
ftime(&t_now);
return (t_now.time - t_start.time) * 1000 + (t_now.millitm - t_start.millitm);
}
void delay( unsigned long ms ) {
unsigned long start = millis();
while(millis() - start < ms){}
}
void initialize_mock_arduino() {
ftime(&t_start);
}
Я использую следующий макет для создания читабельного вывода, когда мой код записывает двоичные данные на аппаратное последовательное устройство.
fake_serial.h
#include <iostream>
class FakeSerial {
public:
void begin(unsigned long);
void end();
size_t write(const unsigned char*, size_t);
};
extern FakeSerial Serial;
fake_serial.cpp
#include <cstring>
#include <iostream>
#include <iomanip>
#include "fake_serial.h"
void FakeSerial::begin(unsigned long speed) {
return;
}
void FakeSerial::end() {
return;
}
size_t FakeSerial::write( const unsigned char buf[], size_t size ) {
using namespace std;
ios_base::fmtflags oldFlags = cout.flags();
streamsize oldPrec = cout.precision();
char oldFill = cout.fill();
cout << "Serial::write: ";
cout << internal << setfill('0');
for( unsigned int i = 0; i < size; i++ ){
cout << setw(2) << hex << (unsigned int)buf[i] << " ";
}
cout << endl;
cout.flags(oldFlags);
cout.precision(oldPrec);
cout.fill(oldFill);
return size;
}
FakeSerial Serial;
и, наконец, актуальная тестовая программа:
#include "mock_arduino.h"
using namespace std;
void millis_test() {
unsigned long start = millis();
cout << "millis() test start: " << start << endl;
while( millis() - start < 10000 ) {
cout << millis() << endl;
sleep(1);
}
unsigned long end = millis();
cout << "End of test - duration: " << end - start << "ms" << endl;
}
void delay_test() {
unsigned long start = millis();
cout << "delay() test start: " << start << endl;
while( millis() - start < 10000 ) {
cout << millis() << endl;
delay(250);
}
unsigned long end = millis();
cout << "End of test - duration: " << end - start << "ms" << endl;
}
void run_tests() {
millis_test();
delay_test();
}
int main(int argc, char **argv){
initialize_mock_arduino();
run_tests();
}
Этот пост достаточно длинный, поэтому, пожалуйста, обратитесь к моему проекту на GitHub , чтобы увидеть еще несколько тестов в действии. Я продолжаю свои работы в других ветках, кроме master, поэтому проверяйте эти ветки и на дополнительные тесты.
Я решил написать свои собственные легкие процедуры тестирования, но также доступны более надежные платформы модульных тестов, такие как CppUnit.