มีโอกาสได้เปิดวิชา Embedded Software Testing เลยต้องลองกลับไปเขียนภาษา C และ หาไลบรารีสำหรับทดสอบโปรแกรมภาษาซีมาใช้ มีให้เด็กๆ เลือกเล่นหลายๆ ตัวทั้ง Unity (ไม่ไช้โปรแกรมสร้างเกมส์นะครับ :D) CppUTest แต่มาลงตัวที่ CMocka เพราะสามารถใช้ทำ Stubs และ Mock ได้ด้วย
โดยส่วนตัวแล้ว Unity ออกแบบมาค่อนข้างดีสำหรับการทดสอบ embedded แค่เวลาคอมไพล์ยังต้องคอมไพล์ไฟล์ภาษาซีของ Unity เข้าไปร่วมด้วยเลยอาจจะใช้งานยากหน่อย ส่วน CppUTest ก็ออกแนว C++ ไป ถึงแม้ว่าจะใช้ทดสอบโคดภาษาซีได้แต่ก็ไม่ได้เพียวซีจริงๆ CMocka ไม่ได้ออกแบบมาทดสอบ embedded โดยตรงแต่เป็น C ล้วน ใช้การคอมไพล์ช่วยในการสร้าง strub ถึงแม้ว่าเข้าใจยากนิดหน่อย แต่ใช้งานได้ดีทีเดียว
เราจะทดลองสร้าง ฟังก์ชัน calculate โดยรับพรารามิเตอร์ 3 ค่า คือ operator, operand1 และ operand2 แล้วลองเรียก ฟังก์ชัน add แต่ ฟังก์ชัน add เป็น ฟังก์ชันที่เราสร้างขึ้มาเองแล้วถูกเรียกใช้ เราต้อง stub ฟังก์ชัน add เพื่อจะได้ทดสอบเฉพาะการทำงานของฟังก์ชัน calculate เท่านั้น โดยการสร้างฟังก์ชัน __wrap_add ขึ้นมา โดยที่ มี return type และ รับพารามิเตอร์เหมือน ฟังก์ชัน add ทุกอย่าง หากต้องการ stub ฟังก์ชันอื่น นอกจาก add เราก็สารามารถตั้งชื่อโดย __wrap_ เป็นต้น
จากนั้นเราสร้างฟังก์ชันสำหรับทำกรณีทดสอบขึ้นมาชื่อ test_give_10_and_20_with_plus_should_30 โดยให้ operand1 และ operand2 และค่าที่ควรได้ เท่ากับ 10, 20 และ 30 ตามลำดับ เราต้องให้ค่าด้วยว่า ฟังก์ชัน __wrap_add จะให้ค่าถูกต้องเมื่อถูกแทนที่ด้วยฟังก์ชัน add โดยใช้ expect_value แทนการตรวจสอบพารามิเตอร์ที่ได้รับ ต้องตรงกัน และ will_return เพื่อกำหนดค่าให้กับฟังก์ชัน mock_type ว่าจะเป็นค่าอะไร รูปแบบจึงเป็นดังนี้
expect_value(__wrap_add, operand1, input_operand1); expect_value(__wrap_add, operand2, input_operand2); will_return(__wrap_add, expected_result);
เริ่มเลย...
// test_add.c
#include <stdio.h>
#include <setjmp.h>
#include "cmocka.h"
int add(int, int);
int calculate(const char operator, int operand1, int operand2){
int result = 0;
switch(operator) {
case '+':
result = add(operand1, operand2);
break;
}
return result;
}
int __wrap_add(int operand1, int operand2){
check_expected(operand1);
check_expected(operand2);
return mock_type(int);
}
void test_give_10_and_20_with_plus_should_30(void **state){
(void) state;
int result;
int input_operand1 = 10;
int input_operand2 = 20;
char operator = '+';
int expected_result = 30;
expect_value(__wrap_add, operand1, input_operand1);
expect_value(__wrap_add, operand2, input_operand2);
will_return(__wrap_add, expected_result);
result = calculate(operator, input_operand1, input_operand2);
assert_int_equal(expected_result, result);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_give_10_and_20_with_plus_should_30),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
หลังจากนั้นลองคอมไพล์ด้วย พารามิเตอร์ด้านล่าง แล้วลองรันดู
$ gcc test_add.c -Wl,--wrap=add -lcmocka -o test_add $ ./test_add [==========] Running 1 test(s). [ RUN ] test_give_10_and_20_with_plus_should_30 [ OK ] test_give_10_and_20_with_plus_should_30 [==========] 1 test(s) run. [ PASSED ] 1 test(s).
พารามิเตอร์ที่สำคัญสำหรับการคอมไพล์ -Wl,--wrap=add -lcmocka ซึ่งเป็นการบอกให้คอมไพเลอร์รู้ว่า -lcmocka เราจะเชื่อไลบรารี cmocka เข้ามาในโปรแกรม และ --wrap=add ให้คอมไพเลอร์
เปลี่ยนไปใช้ฟังก์ชัน __wrap_add แทน add สังเกตจากไฟล์นี้ ไม่มีการนิยามฟังก์ชัน add มีแค่โปรโตไทป์เท่านั้น ก็สามารถรันโปรแกรมได้ ใครมองหา test harness สำหรับภาษาซีอยู่ลองพิจารณา cmocka ดูนะครับ \o/
ความคิดเห็น
แสดงความคิดเห็น