ข้ามไปที่เนื้อหาหลัก

สร้าง Stubs ภาษา C ด้วย cmocka

มีโอกาสได้เปิดวิชา 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/

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

ตัวเอ๋ยตัวผม

กลอนนี้ใช้เวลาประมาณ 20 นาทีเขียนขึ้นมาในห้องเรียนวิชาสัมนา 1 เพราะอาจารย์อยากให้แนะนำตัวเองเป็นกลอน ไม่รู้จะแต่งว่าไงเลยแต่งออกมาเป็นดอกสร้อย เห็นว่าพอใช้ได้เลยเอามาลงไว้เป็นอนุสร ๏ ตัวเอ๋ยตัวผม นิยมในพระพุทธศาสนา ตั้งจิตตั้งใจตั้งหน้า ใฝ่หาความรู้สู่ตน ตั้งใจศึกษาให้เชี่ยวชาญ ชำนาญในศาสตร์ที่ฝึกฝน ฝึกจิตฝึกสันดานให้เป็นคน เป็นชนในชาติที่ดีเอย ๚ะ๛

บันทึกการจัดงานศพ: พิธีฌาปนกิจศพ

ตรงส่วนนี้คงจะเขียนเกี่ยวกับพิธียกศพออกจากบ้าน และเกร็ดต่างๆ เล็กๆ น้อยๆ เนื่องจากที่จัดงานจะไม่นิยมไว้ศพที่วัด จะไว้ศพที่บ้าน และถ้าเป็นไปได้จะไว้ศพในบ้านเสียด้วยซ่ำ เมื่อถึงวันฌาปนกิจศพ หรือเผาศพ ก็จะมีการเซ่นไหว้ครั้งใหญ่ก่อนเคลื่อนย้ายศพไปวัดเพื่อฌาปนกิจ เครื่องเซ่นไว้จะประกอบไปด้วย ข้าว 5 ถ้วย กับข้าว 5 อย่าง หัวหมู ไก่ต้ม ไข่ต้ม หมูสามชั้นต้ม หมี่เหลืองผัด กุ้ง หอย ปู ปลา ผลไม้ 5 อย่าง ขนมขึ้น เมื่อมีการเซ่นไหว้ทุกครั้งจะต้องมี สัปรด น้ำชา 3 จอก เหล้าขาว 5 จอก(หลานๆ บอกว่าเจ็คไม่กินเหล้าขาว แต่มีคนบอกว่าเป็นการไหว้ตามประเพณี ^^ ) ซึ่งแต่ละอย่างมีความหมาย แต่ผมจำไม่ได้ต้องหาอีกครั้งนึง ตัวอย่างเครื่องเซ่นไหว้ เมื่อถึงพิธีเซ่นไหว้ จะมีการเซ่นไหว้โดยแบ่งออกเป็นคณะ แต่เพื่อความสะดวกและรวบรัดจึงมีการไหว้เพียงไม่กี่คณะ ซึ่งก็เหมือนเดิมคือผู้ที่มีศักดิ์สูงกว่าจะไม่รวมการเซ่นไหว้ครั้งนี้ คณะแรกจะเป็นผู้ไกล้ชิดผู้ตายมากที่สุดเริ่มตั้งแต่ลูกและภรรยา หลังจากนั้นก็จะเป็นน้องๆ แล้วก็หลานๆ และก็มิตรรักและผู้คนที่นับถือผู้ตาย หากเป็นเมื่อสมัยก่อนนั้น ต้องแยกออกเป็นเขย เป็นสะไภ้ ไหว้กันหลายยกหล

ด้วยระลึกถึงคุณย่า บันทึกจากความทรงจำ

บันทึกนี้เขียนขึ้นเพื่อบันทึกความทรงจำของผมที่มีต่อคุณย่าที่ล่วงลับไปแล้วอย่างไม่มีวันหวนคืน คุณย่าเปรียบเหมือนฟางเส้นสุดท้ายที่ร้อยครอบครัวใหญ่ของเราเอาไว้ไม่ให้แตกแยก หลังจากที่เสียคุณปู่ไปเมื่อ 23 ปีก่อน เนื่องจากบริเวณจังหวัดกระบี่ ตรัง พังงา ภูเก็ต มีชาวจีนอาศัยอยู่มาก แต่มักจะเป็นชาวจีนที่อพยพมาไทยนานแล้ว จากการการสังเกตของผม ชาวจีนแถบนี้โดยมากน่าจะเป็นชาว เปอรานากัน หรือชาวจีนที่อพยพมาจากจีนแล้วตั้งถิ่นฐานอยู่ในแหลมลายูหรืออินโดนีเซีย แล้วหลังจากนั้นจึงอพยพมาอาศัยต่อที่ประเทศไทย จากการบอกเล่าของคุณแม่ ก๋งเคยเล่าให้ฟังว่าตอนยังเด็กเคยแจวเรือจ้างอยู่ที่ปีนัง คุณย่าเคยเล่าว่าเป็นชาวฮกเกี้ยน อีกทั้วจากรูปวาดคุณย่าทวดที่มีการเกล้ามวยผม สวมเสื้อคอลึก ส่วนทางบ้านมีการใช้คำเรียกจีนผสมไทยถิ่นใต้อยู่มาก ผู้หญิงทุกคนนิยมสวมผ้าปาเต๊ะ เสื้อลูกไม้ (เสื้อฉลุลายดอกไม้) อาหารการกินเป็นแบบชาวไทยถิ่นใต้ทุกประการ (กินน้ำพริก แกงส้มเก่งกันทุกคน ยกเว้นก๋ง :D) อีกทั้งก๋งเกิดที่ดินแดนแถบนี้ไม่ได้เดินทางมาจากเมืองจีน (บางทีเรียก เตี่ยต่อเตี่ย คือ ทวดมาจากจีน ส่วนสถานที่เกิดไม่แน่ใจว่าเป็นปีนังหรือไทย)