Bạn nên kiểm tra những gì với bài kiểm tra đơn vị?


122

Tôi mới ra trường và bắt đầu học đại học ở đâu đó vào tuần tới. Chúng tôi đã thấy các bài kiểm tra đơn vị, nhưng chúng tôi không sử dụng chúng nhiều; và mọi người nói về họ, vì vậy tôi nghĩ có lẽ tôi nên làm một số.

Vấn đề là, tôi không biết phải kiểm tra cái gì . Tôi có nên kiểm tra trường hợp phổ biến? Các trường hợp cạnh? Làm thế nào để tôi biết rằng một chức năng được bảo hiểm đầy đủ?

Tôi luôn có cảm giác khủng khiếp rằng trong khi một bài kiểm tra sẽ chứng minh rằng một hàm hoạt động trong một trường hợp nhất định, thì nó hoàn toàn vô dụng để chứng minh rằng hàm đó hoạt động, theo thời gian.


Hãy xem Blog của Roy Osherove . Có rất nhiều thông tin về thử nghiệm đơn vị ở đó bao gồm cả video. Ông cũng đã viết một cuốn sách "Nghệ thuật kiểm tra đơn vị" rất hay.
Piers Myers

9
Tôi tự hỏi bạn nghĩ gì về nó sau gần 5 năm? Bởi vì càng ngày tôi càng cảm thấy rằng mọi người nên biết rõ hơn "những gì không nên kiểm tra đơn vị" ngày nay. Phát triển theo hướng hành vi đã phát triển từ những câu hỏi như bạn đã hỏi.
Remigijus Pankevičius

Câu trả lời:


121

Triết lý cá nhân của tôi đã được như vậy:

  1. Kiểm tra trường hợp phổ biến của tất cả mọi thứ bạn có thể. Điều này sẽ cho bạn biết khi nào mã đó bị hỏng sau khi bạn thực hiện một số thay đổi (theo ý kiến ​​của tôi, đó là lợi ích lớn nhất của thử nghiệm đơn vị tự động).
  2. Kiểm tra các trường hợp cạnh của một vài mã phức tạp khác thường mà bạn nghĩ có thể sẽ có lỗi.
  3. Bất cứ khi nào bạn tìm thấy một lỗi, hãy viết một trường hợp thử nghiệm để che nó trước khi sửa nó
  4. Thêm các bài kiểm tra trường hợp cạnh vào mã ít quan trọng hơn bất cứ khi nào ai đó có thời gian để giết.

1
Cảm ơn vì điều này, tôi đã lúng túng ở đây với những câu hỏi tương tự như OP.
Stephen

5
+1, mặc dù tôi cũng sẽ kiểm tra các trường hợp cạnh của bất kỳ chức năng loại thư viện / tiện ích nào để đảm bảo bạn có API logic. ví dụ: điều gì xảy ra khi null được thông qua? Còn đầu vào trống thì sao? Điều này sẽ giúp đảm bảo thiết kế của bạn hợp lý và ghi lại hành vi trường hợp góc.
mikera

7
# 3 có vẻ như là một câu trả lời rất chắc chắn vì đây là một ví dụ thực tế về cách kiểm tra đơn vị có thể giúp ích. Nếu nó phá vỡ một lần, nó có thể phá vỡ một lần nữa.
Ryan Griffith

Mới bắt đầu, tôi thấy tôi không sáng tạo lắm với các bài kiểm tra. Vì vậy, tôi sử dụng chúng như số 3 ở trên, điều này đảm bảo sự an tâm rằng những con bọ đó sẽ không bao giờ bị phát hiện nữa.
ankush981

Câu trả lời của bạn đã được nêu trong bài viết trung bình phổ biến này: hackernoon.com/ từ
BugHunterUK

67

Trong số rất nhiều câu trả lời, do đó, không ai chạm vào phân vùng tương đươngphân tích giá trị biên , các cân nhắc quan trọng trong câu trả lời cho câu hỏi trong tầm tay. Tất cả các câu trả lời khác, mặc dù hữu ích, là định tính nhưng có thể - và tốt hơn - là định lượng ở đây. @fishtoaster cung cấp một số hướng dẫn cụ thể, chỉ cần nhìn trộm dưới vỏ bọc của định lượng thử nghiệm, nhưng phân vùng tương đương và phân tích giá trị biên cho phép chúng tôi làm tốt hơn.

Trong phân vùng tương đương , bạn chia tập hợp tất cả các đầu vào có thể thành các nhóm dựa trên kết quả mong đợi. Bất kỳ đầu vào từ một nhóm sẽ mang lại kết quả tương đương, do đó các nhóm như vậy được gọi là các lớp tương đương . (Lưu ý rằng kết quả tương đương không có nghĩa là kết quả giống hệt nhau.)

Như một ví dụ đơn giản, hãy xem xét một chương trình nên chuyển đổi các ký tự ASCII chữ thường thành các ký tự viết hoa. Các nhân vật khác phải trải qua một sự chuyển đổi danh tính, tức là không thay đổi. Đây là một sự cố có thể thành các lớp tương đương:

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | 0-9!@#,/"... | 0-9!@#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

Cột cuối cùng báo cáo số lượng các trường hợp thử nghiệm nếu bạn liệt kê tất cả chúng. Về mặt kỹ thuật, theo quy tắc 1 của @ fishtoaster, bạn sẽ bao gồm 52 trường hợp thử nghiệm - tất cả các trường hợp cho hai hàng đầu tiên được đưa ra ở trên đều thuộc "trường hợp chung". Quy tắc 2 của fishtoaster cũng sẽ thêm một số hoặc tất cả từ hàng 3 và 4 ở trên. Nhưng với kiểm tra phân vùng tương đương, bất kỳ một trường hợp kiểm thử nào trong mỗi lớp tương đương là đủ. Nếu bạn chọn "a" hoặc "g" hoặc "w", bạn đang kiểm tra cùng một đường dẫn mã. Như vậy, bạn có tổng cộng 4 trường hợp thử nghiệm thay vì 52+.

Phân tích giá trị ranh giới đề xuất một sàng lọc nhỏ: về cơ bản nó cho thấy rằng không phải mọi thành viên của một lớp tương đương là tốt, tương đương. Đó là, các giá trị tại các ranh giới cũng nên được coi là xứng đáng với một trường hợp thử nghiệm theo quyền riêng của chúng. (Một cách biện minh dễ dàng cho điều này là lỗi tắt khét tiếng !) Vì vậy, với mỗi lớp tương đương, bạn có thể có 3 đầu vào kiểm tra. Nhìn vào miền đầu vào ở trên - và với một số kiến ​​thức về các giá trị ASCII - tôi có thể đưa ra các đầu vào trường hợp thử nghiệm này:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(Ngay sau khi bạn nhận được nhiều hơn 3 giá trị ranh giới cho thấy bạn có thể muốn suy nghĩ lại về các phân định lớp tương đương ban đầu của mình, nhưng điều này đủ đơn giản để tôi không quay lại để sửa đổi chúng.) 17 trường hợp thử nghiệm - với độ tin cậy cao về phạm vi bảo hiểm hoàn chỉnh - so với 128 trường hợp thử nghiệm để thực hiện thử nghiệm toàn diện. (Không đề cập đến việc tổ hợp ra lệnh rằng thử nghiệm toàn diện đơn giản là không khả thi đối với bất kỳ ứng dụng trong thế giới thực nào!)


3
+1 Đó chính xác là cách tôi trực giác viết bài kiểm tra của mình. Bây giờ tôi có thể đặt tên cho nó :) Cảm ơn đã chia sẻ điều đó.
guillaume31

+1 cho "câu trả lời định tính là hữu ích, nhưng có thể - và tốt hơn - là định lượng"
Jimmy Breck-McKye

Tôi nghĩ rằng đây là một câu trả lời tốt nếu chỉ thị là "làm thế nào tôi có thể nhận được bảo hiểm tốt với các xét nghiệm của mình". Tôi nghĩ sẽ hữu ích khi tìm ra cách tiếp cận thực tế trên đầu trang này - mục tiêu là mọi nhánh của mọi đoạn logic trong mỗi lớp nên được kiểm tra kỹ lưỡng theo cách này?
Kieren Johnstone

18

Có lẽ ý kiến ​​của tôi không quá phổ biến. Nhưng tôi đề nghị bạn nên tiết kiệm với các bài kiểm tra đơn vị. Nếu bạn có quá nhiều bài kiểm tra đơn vị, bạn có thể dễ dàng dành một nửa thời gian hoặc nhiều hơn cho việc duy trì các bài kiểm tra thay vì mã hóa thực tế.

Tôi đề nghị bạn viết bài kiểm tra cho những thứ bạn có cảm giác xấu trong ruột hoặc những thứ rất quan trọng và / hoặc sơ cấp. Các bài kiểm tra đơn vị IMHO không phải là sự thay thế cho kỹ thuật tốt và mã hóa phòng thủ. Hiện tại tôi đang làm việc trong một dự án ít nhiều không thể tin được. Nó thực sự ổn định nhưng là một nỗi đau để tái cấu trúc. Trong thực tế, không ai chạm vào mã này trong một năm và ngăn xếp phần mềm dựa trên đó là 4 năm. Tại sao? Bởi vì nó lộn xộn với các bài kiểm tra đơn vị, chính xác là: Kiểm tra đơn vị và kiểm tra tích hợp tự động hóa. (Bạn đã từng nghe về dưa chuột và những thứ tương tự chưa?) Và đây là phần hay nhất: Phần mềm không thể tin được này đã được phát triển bởi một công ty có nhân viên là người tiên phong trong lĩnh vực phát triển dựa trên thử nghiệm. :CƯỜI MỞ MIỆNG

Vì vậy, đề nghị của tôi là:

  • Bắt đầu viết các bài kiểm tra sau khi bạn phát triển bộ xương cơ bản, nếu không thì tái cấu trúc có thể gây đau đớn. Là một nhà phát triển phát triển cho người khác, bạn không bao giờ có được yêu cầu ngay khi bắt đầu.

  • Hãy chắc chắn rằng bài kiểm tra đơn vị của bạn có thể được thực hiện nhanh chóng. Nếu bạn có các bài kiểm tra tích hợp (như dưa chuột) thì không sao nếu chúng mất nhiều thời gian hơn. Nhưng các bài kiểm tra chạy dài không có gì thú vị, tin tôi đi. (Mọi người quên tất cả lý do tại sao C ++ trở nên ít phổ biến hơn ...)

  • Để lại công cụ TDD này cho các chuyên gia TDD.

  • Và vâng, đôi khi bạn tập trung vào các trường hợp cạnh, đôi khi vào các trường hợp phổ biến, tùy thuộc vào nơi bạn mong đợi bất ngờ. Mặc dù nếu bạn luôn mong đợi những điều bất ngờ, bạn thực sự nên suy nghĩ lại về quy trình làm việc và kỷ luật của bạn. ;-)


2
Bạn có thể cung cấp thêm chi tiết về lý do tại sao các bài kiểm tra làm cho phần mềm này trở thành một nỗi đau để tái cấu trúc?
Mike Partridge

6
Lớn +1. Có các bức tường của các bài kiểm tra đơn vị kiểm tra việc thực hiện thay vì các quy tắc khiến cho bất kỳ thay đổi nào cũng cần
gấp 2-3 lần

9
Giống như mã sản xuất kém bằng văn bản, các bài kiểm tra đơn vị viết kém rất khó để duy trì. "Quá nhiều bài kiểm tra đơn vị" nghe có vẻ như không giữ được DRY; mỗi bài kiểm tra nên giải quyết / chứng minh một phần cụ thể của hệ thống.
Allan

1
Mỗi bài kiểm tra đơn vị nên kiểm tra một điều, vì vậy không có quá nhiều bài kiểm tra đơn vị nhưng, thiếu bài kiểm tra. Nếu bài kiểm tra đơn vị của bạn phức tạp, đó là một vấn đề khác.
liệu

1
-1: Tôi nghĩ rằng bài viết này được viết kém. Có nhiều điều được đề cập và tôi không biết tất cả chúng liên quan như thế nào. Nếu quan điểm của câu trả lời là "kinh tế", thì ví dụ của bạn có liên quan như thế nào? Có vẻ như tình huống ví dụ của bạn (mặc dù thực tế) có các bài kiểm tra đơn vị xấu. Vui lòng giải thích những bài học tôi nên học từ đó và làm thế nào nó giúp tôi tiết kiệm. Ngoài ra, tôi thành thật mà nói, đơn giản là không biết ý của bạn khi bạn nói Leave this TDD stuff to the TDD-experts.
Alexander Bird

8

Nếu bạn đang thử nghiệm đầu tiên với Phát triển theo hướng thử nghiệm, thì phạm vi bảo hiểm của bạn sẽ tăng lên trong phạm vi 90% hoặc cao hơn, bởi vì bạn sẽ không thêm chức năng mà không viết thử nghiệm đơn vị thất bại cho nó.

Nếu bạn đang thêm các bài kiểm tra sau khi thực tế, thì tôi không thể khuyên bạn nên lấy một bản sao Hiệu quả với Mã kế thừa của Michael Feathers và xem xét một số kỹ thuật để thêm các bài kiểm tra vào mã của bạn và cách tái cấu trúc mã của bạn để làm cho nó dễ kiểm tra hơn


Làm thế nào để bạn tính phần trăm bảo hiểm đó? Nó có nghĩa là gì để bao gồm 90% mã của bạn dù sao?
zneak

2
@zneak: có các công cụ bảo hiểm mã sẽ tính toán chúng cho bạn. Một google nhanh chóng cho "phạm vi bảo hiểm mã" sẽ đưa ra một số trong số họ. Công cụ theo dõi các dòng mã được thực thi trong khi chạy thử nghiệm và căn cứ vào tổng số dòng mã trong cụm (ies) để đưa ra tỷ lệ bao phủ.
Steven Evers

-1. Không trả lời câu hỏi:The problem is, I don't know _what_ to test
Alexander Bird

6

Nếu bạn bắt đầu tuân theo các thực tiễn Phát triển theo hướng Thử nghiệm , họ sẽ sắp xếp hướng dẫn bạn qua quy trình và biết những gì cần kiểm tra sẽ đến một cách tự nhiên. Một số nơi để bắt đầu:

Các bài kiểm tra đến trước

Không bao giờ, viết mã trước khi viết bài kiểm tra. Xem Red-Green-Refactor-Lặp lại để được giải thích.

Viết bài kiểm tra hồi quy

Bất cứ khi nào bạn gặp lỗi, hãy viết một testcase và chắc chắn rằng nó thất bại . Trừ khi bạn có thể tái tạo một lỗi thông qua một thử nghiệm thất bại, bạn thực sự không tìm thấy nó.

Red-Green-Refactor-Lặp lại

Màu đỏ : Bắt đầu bằng cách viết một bài kiểm tra cơ bản nhất cho hành vi mà bạn đang cố gắng thực hiện. Hãy nghĩ về bước này khi viết một số mã ví dụ sử dụng lớp hoặc hàm mà bạn đang làm việc. Hãy chắc chắn rằng nó biên dịch / không có lỗi cú pháp và nó bị lỗi . Điều này là hiển nhiên: bạn chưa viết bất kỳ mã nào, vì vậy nó phải thất bại, phải không? Điều quan trọng cần tìm hiểu ở đây là trừ khi bạn thấy bài kiểm tra thất bại ít nhất một lần, bạn không bao giờ có thể chắc chắn rằng nếu nó vượt qua, nó sẽ làm điều đó vì một điều gì đó mà bạn đã làm vì lý do không có thật.

Màu xanh lá cây : Viết mã đơn giản nhất và ngu ngốc mà thực sự làm cho các đường chuyền thử nghiệm. Đừng cố tỏ ra thông minh. Ngay cả khi bạn thấy rằng có một trường hợp cạnh rõ ràng nhưng kiểm tra có tính đến tài khoản, đừng viết mã để xử lý nó (nhưng đừng quên trường hợp cạnh: bạn sẽ cần nó sau). Ý tưởng là mỗi đoạn mã yo viết, mọi if, mọi thứ đều try: ... except: ...phải được chứng minh bằng một trường hợp thử nghiệm. Mã không cần phải thanh lịch, nhanh chóng hoặc tối ưu hóa. Bạn chỉ muốn bài kiểm tra vượt qua.

Refactor : Làm sạch mã của bạn, lấy đúng tên phương thức. Xem thử nghiệm vẫn còn vượt qua. Tối ưu hóa. Chạy thử lại.

Lặp lại : Bạn nhớ trường hợp cạnh mà bài kiểm tra không bao gồm, phải không? Vì vậy, bây giờ là thời điểm quan trọng của nó. Viết một testcase bao gồm tình huống đó, xem nó thất bại, viết một số mã, xem nó vượt qua, tái cấu trúc.

Kiểm tra mã của bạn

Bạn đang làm việc trên một số đoạn mã cụ thể và đây chính xác là những gì bạn muốn kiểm tra. Điều này có nghĩa là bạn không nên kiểm tra các chức năng thư viện, thư viện chuẩn hoặc trình biên dịch của bạn. Ngoài ra, hãy cố gắng tránh thử nghiệm "thế giới". Điều này bao gồm: gọi API web bên ngoài, một số nội dung chuyên sâu về cơ sở dữ liệu, v.v ... Bất cứ khi nào bạn có thể cố gắng giả lập nó (tạo một đối tượng theo cùng giao diện, nhưng trả về dữ liệu tĩnh, được xác định trước).


1
Giả sử tôi đã có sẵn một cơ sở mã làm việc và (theo như tôi có thể thấy), tôi phải làm gì?
zneak

Điều đó có thể khó khăn hơn một chút (tùy thuộc vào cách viết mã). Bắt đầu với các bài kiểm tra hồi quy (chúng luôn có ý nghĩa), sau đó bạn có thể thử viết bài kiểm tra đơn vị để chứng minh với bản thân rằng bạn hiểu mã đang làm gì. Rất dễ bị choáng ngợp bởi số lượng công việc (dường như) phải làm, nhưng: một số bài kiểm tra luôn tốt hơn so với không có bài kiểm tra nào cả.
Ryszard Szopa

3
-1 Tôi không nghĩ đó là một câu trả lời hay cho câu hỏi này . Câu hỏi không phải là về TDD, nó là hỏi về những gì cần kiểm tra khi viết bài kiểm tra đơn vị. Tôi nghĩ rằng một câu trả lời tốt cho câu hỏi thực tế nên áp dụng cho một phương pháp không TDD.
Bryan Oakley

1
Nếu bạn chạm vào nó, kiểm tra nó. Và Clean Code (Robert C Martin) đề nghị bạn viết "bài kiểm tra học tập" cho mã bên thứ 3. Bằng cách đó, bạn học cách sử dụng nó và bạn có các bài kiểm tra trong trường hợp phiên bản mới thay đổi hành vi bạn đang sử dụng.
Roger Willcocks

3

Đối với các bài kiểm tra đơn vị, hãy bắt đầu với kiểm tra rằng nó thực hiện những gì nó được thiết kế để làm. Đó phải là trường hợp đầu tiên bạn viết. Nếu một phần của thiết kế là "nó sẽ ném một ngoại lệ nếu bạn vượt qua rác", hãy kiểm tra điều đó vì đó là một phần của thiết kế.

Bắt đầu với điều đó. Khi bạn có kinh nghiệm thực hiện việc kiểm tra cơ bản nhất đó, bạn sẽ bắt đầu tìm hiểu xem điều đó có đủ hay không và bắt đầu thấy các khía cạnh khác của mã cần kiểm tra.


0

Câu trả lời chứng khoán là "kiểm tra mọi thứ có thể phá vỡ" .

Những gì quá đơn giản để phá vỡ? Các trường dữ liệu, bộ truy cập thuộc tính chết não và trên đầu nồi hơi tương tự. Bất cứ điều gì khác có thể thực hiện một số phần có thể xác định được của một yêu cầu và có thể được hưởng lợi từ việc được thử nghiệm.

Tất nhiên, số dặm của bạn - và thực tiễn môi trường làm việc của bạn - có thể khác nhau.


Được chứ. Vậy những trường hợp nào tôi nên kiểm tra? Trường hợp "bình thường"? Các trường hợp cạnh?
zneak

3
Quy tắc của ngón tay cái? Một hoặc hai ngay giữa con đường vàng, và ngay bên trong và ngay bên ngoài bất kỳ cạnh nào.
Jeffrey Hantin

@JeffreyHantin Đó là "phân tích giá trị biên" trong một câu trả lời khác.
Roger Willcocks
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.