Не запускайте тести на пристрої Arduino або емуляторі
Справа проти тестів на основі мікроконтролерів Device / Emulator / Sim
Існує багато дискусій про те, що означає одиничний тест, і я не дуже намагаюся тут аргументувати. Ця публікація не
каже вам уникати будь-яких практичних тестувань на кінцевому цільовому обладнанні. Я намагаюся зробити точку щодо оптимізації циклу зворотного зв’язку щодо вашої розробки, вилучивши цільове обладнання з ваших найбільш звичних та найчастіших тестів. Передбачається, що одиниці тестування значно менші, ніж весь проект.
Метою одиничного тестування є перевірка якості власного коду. Тести одиниць, як правило, ніколи не повинні перевіряти функціональність факторів, які не є вашим контролем.
Подумайте про це так: Навіть якщо ви перевіряли функціональність бібліотеки 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 щоб переглянути ще кілька тестових випадків. Я продовжую свою роботу в інших галузях, крім головних, тому перевіряйте ці гілки і на додаткові тести.
Я вирішив написати власну легку тестову процедуру, але більш надійні рамки тестування одиниць, такі як CppUnit, також доступні.