Nên tổ chức mã kiểm tra đơn vị C ++ như thế nào để đạt hiệu quả kiểm tra đơn vị tối đa?


47
  • Câu hỏi này không phải là về Khung kiểm tra đơn vị.
  • Câu hỏi này không phải là về bài kiểm tra đơn vị.
  • Câu hỏi này là về nơi để đặt mã UT được viết và cách / khi / nơi để biên dịch và chạy nó.

Khi làm việc hiệu quả với Bộ luật kế thừa , Michael Feathers khẳng định rằng

kiểm tra đơn vị tốt ... chạy nhanh

và đó

Một bài kiểm tra đơn vị mất 1/10 giây để chạy là một bài kiểm tra đơn vị chậm.

Tôi nghĩ rằng những định nghĩa này có ý nghĩa. Tôi cũng nghĩ rằng họ ngụ ý rằng bạn phải giữ một bộ Bài kiểm tra đơn vị và một bộ Bài kiểm tra mã đó mất nhiều thời gian hơn , nhưng tôi đoán đó là cái giá bạn phải trả khi chỉ gọi một bài kiểm tra đơn vị nếu nó chạy rất nhanh .

Rõ ràng vấn đề trong C ++ là để "chạy" ( các ) Bài kiểm tra đơn vị của bạn, bạn phải:

  1. Chỉnh sửa mã của bạn (sản xuất hoặc Kiểm tra đơn vị, tùy thuộc vào "chu kỳ" bạn đang ở)
  2. Biên dịch
  3. Liên kết
  4. Bắt đầu kiểm tra đơn vị thực thi ( s )

Chỉnh sửa (sau khi bỏ phiếu gần lạ) : Trước khi đi vào chi tiết, tôi sẽ thử tóm tắt điểm ở đây:

Làm thế nào để mã thử nghiệm đơn vị C ++ có thể được tổ chức một cách hiệu quả, sao cho vừa hiệu quả để chỉnh sửa mã (thử nghiệm) vừa chạy mã thử nghiệm?


Các đầu tiên vấn đề sau đó là quyết định nơi để đặt mã Unit Test để:

  • đó là "tự nhiên" để chỉnh sửa và xem nó kết hợp với mã sản xuất được liên kết.
  • thật dễ dàng / nhanh chóng để bắt đầu chu trình biên dịch cho đơn vị bạn đang thay đổi

Vấn đề thứ hai , liên quan, sau đó là những gì cần biên dịch để phản hồi là tức thời.

Tùy chọn cực đoan:

  • Mỗi Đơn vị-Kiểm tra-Kiểm tra-Đơn vị sống trong một tệp cpp riêng và tệp cpp này được biên dịch + liên kết riêng (cùng với tệp đơn vị mã nguồn mà nó kiểm tra) thành một tệp thực thi duy nhất sau đó chạy một Kiểm tra đơn vị này.
    • (+) Điều này giảm thiểu thời gian khởi động (biên dịch + liên kết!) Cho Đơn vị kiểm tra duy nhất.
    • (+) Bài kiểm tra chạy siêu nhanh, vì nó chỉ kiểm tra một đơn vị.
    • (-) Thực hiện toàn bộ bộ phần mềm sẽ cần bắt đầu một loạt các quy trình. Có thể là một vấn đề để quản lý.
    • (-) Chi phí quá trình bắt đầu sẽ hiển thị
  • Mặt khác sẽ có - một - một tệp cpp cho mỗi thử nghiệm, nhưng tất cả các tệp cpp thử nghiệm (cùng với mã mà chúng kiểm tra!) Được liên kết thành một tệp thực thi (mỗi mô-đun / mỗi dự án / chọn lựa của bạn).
    • (+) Thời gian biên dịch vẫn ổn, vì chỉ mã được thay đổi sẽ biên dịch.
    • (+) Thực hiện toàn bộ bộ phần mềm rất dễ dàng, vì chỉ có một exe để chạy.
    • (-) Bộ sẽ mất nhiều thời gian để liên kết, vì mỗi lần biên dịch lại của bất kỳ đối tượng nào sẽ kích hoạt liên kết lại.
    • (-) (?) Bộ đồ sẽ mất nhiều thời gian hơn để chạy, mặc dù nếu tất cả các Bài kiểm tra đơn vị đều nhanh, thời gian sẽ ổn.

Vì vậy, các bài kiểm tra đơn vị C ++ trong thế giới thực được xử lý như thế nào? Nếu tôi chỉ chạy công cụ đó hàng đêm / hàng giờ, phần thứ hai không thực sự quan trọng, nhưng phần đầu tiên, cụ thể là làm thế nào để "ghép" mã UT với mã sản xuất, để các nhà phát triển giữ nguyên cả hai Tôi luôn luôn tập trung vào vấn đề. (Và nếu các nhà phát triển tập trung vào mã UT, họ sẽ muốn chạy nó, điều này đưa chúng ta trở lại phần hai.)

Những câu chuyện và kinh nghiệm trong thế giới thực được đánh giá cao!

Ghi chú:

  • Câu hỏi này cố tình rời khỏi nền tảng không xác định và hệ thống / dự án.
  • Câu hỏi được gắn thẻ UT & C ++ là một nơi tuyệt vời để bắt đầu, nhưng thật không may, quá nhiều câu hỏi, và đặc biệt là câu trả lời, quá tập trung vào các chi tiết hoặc vào các khung cụ thể.
  • Cách đây một thời gian, tôi đã trả lời một câu hỏi tương tự về cấu trúc để kiểm tra đơn vị boost. Tôi thấy cấu trúc này thiếu cho các bài kiểm tra đơn vị nhanh, "thực". Và tôi thấy câu hỏi khác quá hẹp, do đó câu hỏi mới này.

6
Một biến chứng nữa được đưa ra bởi thành ngữ C ++ về việc di chuyển càng nhiều lỗi càng tốt để biên dịch thời gian - một bộ kiểm tra đơn vị tốt thường cần để có thể kiểm tra rằng việc sử dụng nhất định không biên dịch được.

3
@Closers: Bạn có thể vui lòng cung cấp các đối số khiến bạn đóng câu hỏi này không?
Luc Touraille

Tôi không hiểu tại sao điều này phải được đóng lại. :-(Bạn đang ở đâu để tìm câu trả lời cho những câu hỏi như vậy nếu không có trong diễn đàn này?

2
@Joe: Tại sao tôi cần một bài kiểm tra đơn vị để tìm xem liệu cái gì đó sẽ biên dịch khi trình biên dịch sẽ cho tôi biết điều đó không?
David Thornley

2
@David: Bởi vì bạn muốn chắc chắn rằng nó không biên dịch . Ví dụ nhanh sẽ là một đối tượng đường ống chấp nhận đầu vào và tạo ra đầu ra Pipeline<A,B>.connect(Pipeline<B,C>)nên biên dịch trong khi Pipeline<A,B>.connect(Pipeline<C,D>)không nên biên dịch: Loại đầu ra của giai đoạn đầu tiên không tương thích với loại đầu vào của giai đoạn thứ hai.
sebastiangeiger

Câu trả lời:


6

Chúng tôi có tất cả các bài kiểm tra đơn vị (cho một mô-đun) trong một lần thực thi. Các xét nghiệm được đưa vào nhóm. Tôi có thể thực hiện một thử nghiệm duy nhất (hoặc một số thử nghiệm) hoặc một nhóm thử nghiệm bằng cách chỉ định tên (thử nghiệm / nhóm) trên dòng lệnh của người chạy thử nghiệm. Hệ thống xây dựng có thể chạy nhóm "Xây dựng", bộ phận kiểm tra có thể chạy "Tất cả". Nhà phát triển có thể đặt một số thử nghiệm vào một nhóm như "BUG1234" với 1234 là số theo dõi vấn đề của vụ án mà anh ta đang làm việc.


6

Đầu tiên, tôi không đồng ý với "1) Chỉnh sửa mã (sản xuất) và Kiểm tra đơn vị của bạn". Bạn chỉ nên sửa đổi từng cái một, nếu không, nếu kết quả thay đổi, bạn sẽ không biết cái nào gây ra nó.

Tôi muốn đặt các bài kiểm tra đơn vị trong một cây thư mục che bóng cây chính. Nếu tôi có /sources/componentA/alpha/foo.cc/objects/componentA/beta/foo.o, sau đó tôi muốn một cái gì đó như /UTest_sources/componentA/alpha/test_foo.cc/UTest_objects/componentA/beta/test_foo.o. Tôi sử dụng cùng một cây bóng cho các đối tượng sơ khai / giả và bất kỳ nguồn nào khác mà các bài kiểm tra cần. Sẽ có một số trường hợp cạnh, nhưng sơ đồ này đơn giản hóa mọi thứ rất nhiều. Một macro biên tập tốt có thể dễ dàng kéo lên nguồn kiểm tra cùng với nguồn chủ đề. Một hệ thống tốt xây dựng (ví dụ gnumake) có thể biên dịch cả hai và chạy thử nghiệm với một lệnh (ví dụ make test_foo), và có thể quản lý một bazillion quá trình như vậy - chỉ là những người có nguồn đã thay đổi kể từ khi họ đã được thử nghiệm cuối cùng - khá dễ dàng (Tôi có không bao giờ thấy chi phí bắt đầu các quá trình này là một vấn đề, đó là O (N)).

Trong cùng một khung, bạn có thể có các thử nghiệm quy mô lớn hơn (không còn thử nghiệm đơn vị nữa) liên kết nhiều đối tượng với nhau và chạy nhiều thử nghiệm. Mẹo nhỏ là sắp xếp các thử nghiệm này theo thời gian chúng xây dựng / chạy và đưa chúng vào lịch trình hàng ngày của bạn cho phù hợp. Chạy thử nghiệm một giây hoặc ít hơn bất cứ khi nào bạn cảm thấy thích nó; bắt đầu bài kiểm tra mười giây và kéo dài; kiểm tra năm phút và nghỉ ngơi; kiểm tra nửa giờ và đi ăn trưa; kiểm tra sáu giờ và về nhà. Nếu bạn thấy rằng bạn đang lãng phí rất nhiều thời gian, ví dụ như làm lại một bài kiểm tra lớn sau khi chỉ thay đổi một tệp nhỏ, bạn đã làm sai - ngay cả khi liên kết là tức thời, bạn vẫn sẽ chạy một bài kiểm tra dài khi nó đã không được gọi cho.


quảng cáo (1) - vâng, đó là cụm từ chậm chạp
Martin Ba
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.