Các ví dụ kiểm tra đơn vị tốt cho các nhà phát triển C nhúng [đã đóng]


20

Tôi sẽ nói chuyện với bộ phận của tôi vào tuần tới về thử nghiệm đơn vị và phát triển dựa trên thử nghiệm. Là một phần của điều này, tôi sẽ trình bày một số ví dụ trong thế giới thực từ một số mã tôi đã viết gần đây, nhưng tôi cũng muốn hiển thị một số ví dụ rất đơn giản mà tôi sẽ viết trong bài nói chuyện.

Tôi đã tìm kiếm trên web các ví dụ hay, nhưng tôi đã vật lộn để tìm bất kỳ thứ gì đặc biệt áp dụng cho lĩnh vực phát triển của chúng tôi. Hầu như tất cả các phần mềm chúng tôi viết là các hệ thống điều khiển nhúng sâu chạy trên các vi điều khiển nhỏ. Có rất nhiều mã C có thể dễ dàng áp dụng cho thử nghiệm đơn vị (tôi sẽ nói về thử nghiệm đơn vị trên PC thay vì trên chính mục tiêu) miễn là bạn tránh xa lớp 'dưới cùng': nội dung nói trực tiếp đến các thiết bị ngoại vi vi điều khiển. Tuy nhiên, hầu hết các ví dụ tôi đã tìm thấy có xu hướng dựa trên xử lý chuỗi (ví dụ: ví dụ tuyệt vời về Lặn vào Python Roman) và vì chúng tôi hầu như không sử dụng chuỗi này nên điều này không thực sự phù hợp (về các hàm thư viện duy nhất mà mã của chúng tôi thường sử dụng là memcpy, memcmpmemset,strcat hoặc biểu thức chính quy không hoàn toàn đúng).

Vì vậy, về câu hỏi: xin vui lòng bất cứ ai có thể cung cấp một số ví dụ tốt về các chức năng mà tôi có thể sử dụng để trình diễn thử nghiệm đơn vị trong phiên trực tiếp? Một câu trả lời tốt trong quan điểm (có thể thay đổi) của tôi có lẽ sẽ là:

  • Một hàm đủ đơn giản để bất kỳ ai (ngay cả những người chỉ thỉnh thoảng viết mã) có thể hiểu được;
  • Một hàm không xuất hiện vô nghĩa (nghĩa là tìm ra chẵn lẻ hoặc CRC có thể tốt hơn một hàm nhân hai số với nhau và thêm một hằng số ngẫu nhiên);
  • Một chức năng đủ ngắn để viết trước phòng của mọi người (tôi có thể tận dụng nhiều bảng ghi của Vim để giảm lỗi ...);
  • Một hàm lấy số, mảng, con trỏ hoặc cấu trúc làm tham số và trả về một cái gì đó tương tự, thay vì xử lý chuỗi;
  • Một chức năng mà có một lỗi đơn giản (ví dụ >chứ không phải >=) đó là dễ dàng để đưa vào đó sẽ vẫn làm việc trong hầu hết các trường hợp, nhưng sẽ phá vỡ với một số trường hợp cụ thể cạnh: dễ dàng để xác định và sửa chữa với một thử nghiệm đơn vị.

Có suy nghĩ gì không?

Mặc dù có thể không liên quan, nhưng các bài kiểm tra có thể sẽ được viết bằng C ++ bằng Google Test Framework: tất cả các tiêu đề của chúng tôi đã có #ifdef __cplusplus extern "C" {trình bao bọc xung quanh chúng; điều này đã làm việc tốt với các bài kiểm tra tôi đã làm cho đến nay.


Lấy "vấn đề" ở đây là đưa ra một bài thuyết trình để bán TDD cho ban quản lý, điều này đối với tôi có vẻ phù hợp với định dạng mong muốn một cách hợp lý. OP dường như đang yêu cầu các giải pháp hiện có cho vấn đề này.
Technophile

Câu trả lời:


15

Đây là một chức năng đơn giản được cho là tạo tổng kiểm tra trên các byte len .

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;
}

Nó có một lỗi hàng rào: trong câu lệnh for, nên kiểm tra i < len.

Điều thú vị là, nếu bạn áp dụng nó cho một chuỗi văn bản như thế này ...

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

bạn sẽ nhận được "câu trả lời đúng"! Đó là bởi vì byte bổ sung được kiểm tra là bộ kết thúc chuỗi số không. Vì vậy, bạn có thể kết thúc việc đưa chức năng tổng kiểm tra này vào mã, và thậm chí có thể vận chuyển với nó, và không bao giờ nhận thấy vấn đề - đó là cho đến khi bạn bắt đầu áp dụng nó cho một thứ khác ngoài chuỗi văn bản.

Đây là một bài kiểm tra đơn vị đơn giản sẽ đánh dấu lỗi này (hầu hết thời gian ... :-)

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);
    }
}

Rất tốt! Đây chỉ là loại câu trả lời mà tôi đã hy vọng: cảm ơn bạn.
DrAl

Khi bạn tạo bộ đệm bạn đã có rác trong đoạn bộ nhớ đó, có thực sự cần thiết để khởi tạo nó với các số ngẫu nhiên không?
Rắn Sanders

@SnakeSanders Tôi sẽ nói có, bởi vì bạn muốn các bài kiểm tra đơn vị càng mang tính quyết định càng tốt. Nếu trình biên dịch bạn sử dụng xảy ra để đặt 0 ở đó trên máy phát triển của bạn và 10 trên máy thử nghiệm của bạn, bạn sẽ có một thời gian khủng khiếp khi tìm thấy lỗi. Tôi nghĩ rằng làm cho nó phụ thuộc vào thời gian thay vì một hạt giống cố định là một ý tưởng tồi, vì lý do tương tự.
Andrew nói Phục hồi

Dựa vào các hành vi không xác định trong một bài kiểm tra đơn vị là một ý tưởng tồi. Một bài kiểm tra dễ vỡ sẽ khiến bạn đau đầu sớm hay muộn ...
sigy

2

Điều gì về việc thực hiện một chức năng sắp xếp như sắp xếp bong bóng ? Khi bạn có chức năng sắp xếp hoạt động, bạn có thể tiếp tục tìm kiếm nhị phân , điều này cũng tốt cho việc giới thiệu thử nghiệm đơn vị và TDD.

Sắp xếp và tìm kiếm phụ thuộc vào so sánh rất dễ bị sai. Nó cũng liên quan đến việc hoán đổi con trỏ xung quanh phải được thực hiện một cách cẩn thận. Cả hai đều dễ bị lỗi, vì vậy hãy thoải mái để gây rối :)

Một vài ý tưởng nữa:

  • Kiểm tra đơn vị giúp rất nhiều khi thực hiện tái cấu trúc. Vì vậy, một khi sắp xếp bong bóng của bạn hoạt động, bạn có thể thay đổi nó thành một loại mạnh hơn như thế qsort, và các bài kiểm tra vẫn sẽ vượt qua, chứng minh chức năng sắp xếp mới của bạn cũng hoạt động.
  • Sắp xếp dễ dàng để kiểm tra, kết quả được sắp xếp hoặc không, điều này làm cho nó trở thành một ứng cử viên tốt.
  • Tương tự cho việc tìm kiếm; nó tồn tại hoặc không.
  • Viết các bài kiểm tra để sắp xếp sẽ mở ra các cuộc thảo luận như loại đầu vào nào được sử dụng để kiểm tra (các phần tử bằng không, đầu vào ngẫu nhiên, các mục trùng lặp, các mảng lớn, v.v.).

Bạn có bất kỳ đề xuất cụ thể cho một lỗi đơn giản sẽ cho thấy cách kiểm tra làm cho cuộc sống dễ dàng hơn không?
DrAl

@DrAl: Cập nhật câu trả lời của tôi với điều đó.
Martin Wickman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.