Не запускайте юнит-тесты на устройстве 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.