อย่าเรียกใช้การทดสอบหน่วยบนอุปกรณ์ Arduino หรือตัวจำลอง
กรณีกับการทดสอบอุปกรณ์ไมโครคอนโทรลเลอร์ / จำลอง / ซิมที่ใช้
มีการถกเถียงกันมากมายเกี่ยวกับความหมายของการทดสอบหน่วยและฉันไม่ได้พยายามโต้เถียงเกี่ยวกับเรื่องนี้ โพสต์นี้ไม่ได้
บอกให้คุณหลีกเลี่ยงการทดสอบเชิงปฏิบัติทั้งหมดเกี่ยวกับฮาร์ดแวร์เป้าหมายสูงสุด ฉันกำลังพยายามหาจุดที่ทำให้วงจรการตอบรับการพัฒนาของคุณดีที่สุดโดยกำจัดฮาร์ดแวร์เป้าหมายของคุณออกจากการทดสอบทางโลกและบ่อยครั้งที่สุด หน่วยภายใต้การทดสอบจะถือว่ามีขนาดเล็กกว่าโครงการทั้งหมด
วัตถุประสงค์ของการทดสอบหน่วยคือการทดสอบคุณภาพของรหัสของคุณเอง การทดสอบหน่วยโดยทั่วไปไม่ควรทดสอบการทำงานของปัจจัยที่อยู่นอกเหนือการควบคุมของคุณ
คิดแบบนี้: แม้ว่าคุณจะต้องทดสอบการทำงานของห้องสมุด Arduino ฮาร์ดแวร์ไมโครคอนโทรลเลอร์หรืออีมูเลเตอร์มันเป็นไปไม่ได้อย่างแน่นอนสำหรับผลการทดสอบดังกล่าวที่จะบอกคุณเกี่ยวกับคุณภาพของงานของคุณเอง ดังนั้นจึงมีคุณค่าและมีประสิทธิภาพมากขึ้นในการเขียนการทดสอบหน่วยที่ไม่ได้ทำงานบนอุปกรณ์เป้าหมาย (หรืออุปกรณ์จำลอง)
การทดสอบฮาร์ดแวร์เป้าหมายเป็นประจำมีรอบการทำงานที่ช้าอย่างเจ็บปวด:
- ปรับแต่งรหัสของคุณ
- รวบรวมและอัพโหลดไปยังอุปกรณ์ Arduino
- สังเกตพฤติกรรมและเดาว่ารหัสของคุณทำในสิ่งที่คุณคาดหวังหรือไม่
- ทำซ้ำ
ขั้นตอนที่ 3 เป็นสิ่งที่น่ารังเกียจอย่างยิ่งหากคุณคาดว่าจะได้รับข้อความวินิจฉัยผ่านพอร์ตอนุกรม แต่โครงการของคุณจำเป็นต้องใช้พอร์ตอนุกรมฮาร์ดแวร์ของ Arduino เท่านั้น หากคุณคิดว่า SoftwareSerial library อาจช่วยได้คุณควรรู้ว่าการทำเช่นนั้นมีแนวโน้มที่จะรบกวนการทำงานใด ๆ ที่ต้องใช้เวลาที่แม่นยำเช่นสร้างสัญญาณอื่น ๆ ในเวลาเดียวกัน ปัญหานี้เกิดขึ้นกับฉัน
อีกครั้งถ้าคุณต้องทดสอบภาพร่างของคุณโดยใช้อีมูเลเตอร์และรูทีนเวลาที่สำคัญของคุณจะวิ่งอย่างสมบูรณ์แบบจนกว่าคุณจะอัปโหลดไปยัง 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);
}
ฉันใช้ mock-up ต่อไปนี้เพื่อสร้างเอาต์พุตที่สามารถอ่านได้เมื่อโค้ดของฉันเขียนข้อมูลไบนารีไปยังอุปกรณ์ซีเรียลฮาร์ดแวร์
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 อีกด้วย