ตัวอย่างการทดสอบหน่วยที่ดีสำหรับนักพัฒนา C ฝังตัว [ปิด]


20

ฉันจะพูดคุยกับแผนกของฉันในสัปดาห์หน้าเกี่ยวกับการทดสอบหน่วยและการพัฒนาที่เน้นการทดสอบ เป็นส่วนหนึ่งของเรื่องนี้ฉันจะแสดงตัวอย่างจริงจากรหัสบางอย่างที่ฉันเขียนเมื่อเร็ว ๆ นี้ แต่ฉันยังต้องการแสดงตัวอย่างง่ายๆที่ฉันจะเขียนในการพูดคุย

ฉันค้นหาตัวอย่างที่ดีจากเว็บ แต่ฉันพยายามหาสิ่งที่เหมาะสมกับการพัฒนาของเราโดยเฉพาะ ซอฟต์แวร์เกือบทั้งหมดที่เราเขียนนั้นเป็นระบบควบคุมฝังตัวลึกที่ทำงานบนไมโครคอนโทรลเลอร์ขนาดเล็ก มีรหัส C จำนวนมากที่ใช้งานได้ง่ายกับการทดสอบหน่วย (ฉันจะพูดถึงการทดสอบหน่วยบนพีซีแทนที่จะเป็นเป้าหมายเอง) ตราบใดที่คุณยังไม่ชัดเจนในเลเยอร์ 'ก้น': สิ่งที่พูดถึงโดยตรง ไปยังอุปกรณ์ต่อพ่วงไมโครคอนโทรลเลอร์ อย่างไรก็ตามตัวอย่างส่วนใหญ่ที่ฉันพบมักใช้การประมวลผลแบบสตริง (เช่นตัวอย่างตัวเลข Dive Into Python เลขโรมันที่ยอดเยี่ยม) และเนื่องจากเราแทบจะไม่เคยใช้สตริงสิ่งนี้จึงไม่เหมาะ (เกี่ยวกับฟังก์ชันไลบรารีเท่านั้นโดยทั่วไปแล้วโค้ดของเราใช้ มีmemcpy, memcmpและmemset,strcat หรือนิพจน์ทั่วไปไม่ถูกต้อง)

ดังนั้นในคำถาม: ทุกคนสามารถเสนอตัวอย่างที่ดีของฟังก์ชั่นที่ฉันสามารถใช้เพื่อสาธิตการทดสอบหน่วยในเซสชั่นสดได้หรือไม่? คำตอบที่ดีในความเห็น (อาจมีการเปลี่ยนแปลง) ของฉันอาจจะ:

  • ฟังก์ชั่นที่เรียบง่ายพอที่ทุกคน (แม้กระทั่งผู้ที่เขียนโค้ดเป็นครั้งคราว) สามารถเข้าใจได้
  • ฟังก์ชั่นที่ไม่ปรากฏว่าไม่มีจุดหมาย (เช่นการทำพาริตีหรือซีอาร์ซีน่าจะดีกว่าฟังก์ชั่นที่คูณสองตัวเลขด้วยกันและเพิ่มค่าคงที่แบบสุ่ม);
  • ฟังก์ชั่นที่สั้นพอที่จะเขียนต่อหน้าผู้คน (ฉันอาจใช้ประโยชน์จากคลิปบอร์ดมากมายของ Vim เพื่อลดข้อผิดพลาด ... );
  • ฟังก์ชั่นที่ใช้ตัวเลขอาร์เรย์พอยน์เตอร์หรือโครงสร้างเป็นพารามิเตอร์และส่งคืนสิ่งที่คล้ายกันมากกว่าการจัดการสตริง
  • ฟังก์ชั่นที่มีข้อผิดพลาดง่าย ๆ (เช่น>มากกว่า>=) ซึ่งง่ายต่อการใส่ในนั้นจะยังคงทำงานได้ในกรณีส่วนใหญ่ แต่จะแบ่งกับกรณีขอบบาง: ง่ายต่อการระบุและแก้ไขด้วยการทดสอบหน่วย

ความคิดใด ๆ

แม้ว่ามันอาจจะไม่เกี่ยวข้อง แต่การทดสอบตัวเองอาจจะเขียนใน C ++ โดยใช้ Google Test Framework: ส่วนหัวของเราทุกคนมี#ifdef __cplusplus extern "C" {เสื้อคลุมรอบแล้ว สิ่งนี้ใช้ได้ดีกับการทดสอบที่ฉันได้ทำไปแล้ว


การที่ "ปัญหา" ที่นี่เกิดขึ้นพร้อมกับการนำเสนอเพื่อขาย TDD ให้กับฝ่ายบริหารนี่ดูเหมือนว่าฉันจะเข้ากับรูปแบบที่ต้องการได้ดีพอสมควร OP ดูเหมือนว่าจะขอวิธีแก้ไขปัญหาที่มีอยู่
Technophile

คำตอบ:


15

นี่คือฟังก์ชั่นง่าย ๆ ที่ควรสร้าง checksum มากกว่าlen bytes

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

แต่ก็มีข้อผิดพลาด fencepost: i < lenในคำสั่งการทดสอบที่ควรจะเป็น

ความสนุกคืออะไรถ้าคุณใช้กับสตริงข้อความเช่นนี้ ...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

คุณจะได้รับ "คำตอบที่ถูกต้อง"! นั่นเป็นเพราะไบต์พิเศษที่ถูกตรวจสอบคือจุดสิ้นสุดของสตริงที่เป็นศูนย์ ดังนั้นคุณสามารถปิดฟังก์ชั่นการตรวจสอบนี้ลงในโค้ดและอาจส่งไปพร้อมกับมันและไม่เคยสังเกตเห็นปัญหานั่นคือจนกว่าคุณจะเริ่มนำไปใช้กับสิ่งอื่นนอกเหนือจากสตริงข้อความ

นี่คือการทดสอบหน่วยง่ายๆที่จะติดธงข้อบกพร่องนี้ (ส่วนใหญ่ ... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}

ดีมาก! นี่เป็นเพียงคำตอบที่ฉันหวังไว้: ขอบคุณ
DrAl

เมื่อคุณสร้างบัฟเฟอร์คุณมีขยะในหน่วยความจำอันนั้นแล้วจำเป็นหรือไม่ที่จะต้องเริ่มต้นด้วยตัวเลขสุ่ม?
Snake Sanders

@SandakeSanders ฉันจะบอกว่าใช่เพราะคุณต้องการทดสอบหน่วยที่จะกำหนดได้มากที่สุด หากคอมไพเลอร์ที่คุณใช้เกิดขึ้นเพื่อใส่ 0 ไว้ในเครื่องนักพัฒนาของคุณและ 10 ในเครื่องทดสอบของคุณคุณจะมีเวลาหาข้อผิดพลาด ฉันคิดว่าการทำให้มันขึ้นอยู่กับเวลาแทนที่จะเป็นเมล็ดตายตัวเป็นความคิดที่ไม่ดีด้วยเหตุผลเดียวกัน
แอนดรูกล่าวว่า Reinstate Monica

การใช้พฤติกรรมที่ไม่ได้กำหนดไว้ในการทดสอบหน่วยเป็นความคิดที่ไม่ดี การทดสอบที่ไม่สม่ำเสมอจะทำให้คุณปวดหัวไม่ช้าก็เร็ว ...
sigy

2

สิ่งที่เกี่ยวกับการใช้ฟังก์ชั่นการจัดเรียงเช่นการเรียงลำดับฟอง ? เมื่อคุณมีฟังก์ชั่นการเรียงลำดับแล้วคุณสามารถดำเนินการค้นหาไบนารีต่อไปซึ่งดีพอสำหรับการแนะนำการทดสอบหน่วยและ TDD

การเรียงลำดับและการค้นหาขึ้นอยู่กับการเปรียบเทียบที่ผิดพลาดได้ง่าย นอกจากนี้ยังเกี่ยวข้องกับการสลับพอยน์เตอร์ที่ต้องทำด้วยความระมัดระวัง ทั้งสองมีแนวโน้มที่จะเกิดข้อผิดพลาดดังนั้นอย่าลังเล :)

แนวคิดเพิ่มเติมไม่กี่:

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

คุณมีคำแนะนำเฉพาะสำหรับข้อผิดพลาดง่ายๆที่จะแสดงว่าการทดสอบทำให้ชีวิตง่ายขึ้นหรือไม่
DrAl

@DrAl: อัปเดตคำตอบของฉันด้วย
Martin Wickman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.