Được viết thủ công kiểm tra đơn vị Bằng chứng bằng ví dụ?


9

Chúng tôi biết rằng viết bài kiểm tra JUnit thể hiện một đường dẫn cụ thể thông qua mã của bạn.

Một trong những cộng sự của tôi đã nhận xét:

Viết thủ công các bài kiểm tra đơn vị là Bằng chứng Ví dụ .

Anh ta đến từ nền tảng của Haskell có các công cụ như Quickcheck và khả năng suy luận về hành vi của chương trình với các loại .

Hàm ý của ông là có rất nhiều kết hợp đầu vào khác chưa được thử nghiệm bằng phương pháp này mà mã của bạn không được kiểm tra.

Câu hỏi của tôi là: Có phải viết thủ công các bài kiểm tra đơn vị Proof By example không?


3
Không, không viết / sử dụng bài kiểm tra. Khẳng định rằng các bài kiểm tra đơn vị của bạn là bằng chứng cho thấy không có gì sai với chương trình là Bằng chứng bằng ví dụ (một khái quát không phù hợp). Các thử nghiệm không phải về tính chính xác của mã chứng minh toán học - các thử nghiệm về bản chất là các kiểm tra thử nghiệm. Đó là một mạng lưới an toàn giúp bạn xây dựng sự tự tin bằng cách cho bạn biết điều gì đó về mã. Nhưng bạn là người phải chọn một chiến lược tốt để thăm dò mã và bạn là người phải diễn giải ý nghĩa của dữ liệu đó.
Filip Milovanović

Câu trả lời:


10

Nếu bạn đang chọn ngẫu nhiên các đầu vào để thử nghiệm, thì tôi cho rằng có thể bạn đang thực hiện một sai lầm logic Bằng chứng Ví dụ.

Nhưng bài kiểm tra đơn vị tốt không bao giờ làm điều đó. Thay vào đó, họ đối phó trong phạm vitrường hợp cạnh.

Ví dụ: nếu bạn đã viết các bài kiểm tra đơn vị cho hàm giá trị tuyệt đối chấp nhận một số nguyên làm đầu vào, bạn sẽ không cần phải kiểm tra mọi giá trị đầu vào có thể để chứng minh rằng mã hoạt động. Để có được bài kiểm tra toàn diện, bạn chỉ cần năm giá trị: -1, 0, 1 và các giá trị tối đa và tối thiểu cho số nguyên đầu vào.

Năm giá trị này kiểm tra mọi phạm vi có thể và trường hợp cạnh của hàm. Bạn không cần phải kiểm tra mọi giá trị đầu vào có thể khác (tức là mọi số mà loại số nguyên có thể biểu thị) để có mức độ tin cậy cao mà hàm hoạt động cho tất cả các giá trị đầu vào.


11
Một người kiểm tra mã vào một quán bar và gọi một cốc bia. 5 loại bia. -1 bia, bia MAX_VALUE, một con gà. một con số không
Neil

2
"5 giá trị" là vô nghĩa. Hãy xem xét một chức năng tầm thường như thế nào int foo(int x) { return 1234/(x - 100); }. Cũng lưu ý rằng (tùy thuộc vào những gì bạn đang kiểm tra), bạn có thể cần đảm bảo rằng đầu vào không hợp lệ ("ngoài phạm vi") trả về kết quả chính xác (ví dụ: `` find_thing (thing) `trả về đúng một số trạng thái" không tìm thấy " nếu điều đó không được tìm thấy).
Brendan

3
@Brendan: Không có gì đáng kể về việc nó là năm giá trị; nó chỉ là năm giá trị trong ví dụ của tôi. Ví dụ của bạn có số lượng thử nghiệm khác nhau vì bạn đang thử nghiệm một chức năng khác. Tôi không nói rằng mọi chức năng đều yêu cầu chính xác năm bài kiểm tra; bạn đã suy luận rằng từ việc đọc câu trả lời của tôi.
Robert Harvey

1
Thư viện kiểm thử tạo thường tốt hơn trong việc kiểm tra các trường hợp cạnh so với bạn. Nếu, ví dụ, bạn đang sử dụng phao nổi thay vì số nguyên, thư viện của bạn cũng sẽ kiểm tra -Inf, Inf, NaN, 1e-100, -1e-100, -0, 2e200... Tôi không muốn phải làm những tất cả bằng tay.
Hovercouch

@Hovercouch: Nếu bạn biết về một người tốt, tôi rất muốn nghe về nó. Cái tốt nhất tôi từng thấy là Pex; mặc dù nó rất không ổn định Hãy nhớ rằng, chúng ta đang nói về các chức năng tương đối đơn giản ở đây. Mọi thứ trở nên khó khăn hơn khi bạn xử lý những thứ như logic kinh doanh thực tế.
Robert Harvey

8

Bất kỳ kiểm thử phần mềm nào cũng giống như "Bằng chứng bằng ví dụ", không chỉ kiểm thử đơn vị bằng cách sử dụng một công cụ như JUnit. Và đó không phải là sự khôn ngoan mới, có một trích dẫn từ Dijkstra từ năm 1960, về cơ bản là giống nhau:

"Thử nghiệm cho thấy sự hiện diện, không phải là không có lỗi"

(chỉ cần thay thế các từ "hiển thị" bằng "bằng chứng"). Tuy nhiên, điều này cũng đúng với các công cụ tạo dữ liệu thử nghiệm ngẫu nhiên. Số lượng đầu vào có thể có cho một chức năng trong thế giới thực thường lớn hơn theo thứ tự cường độ so với số lượng trường hợp thử nghiệm mà người ta có thể tạo ra và xác minh dựa trên kết quả mong đợi trong thời đại vũ trụ, độc lập với phương pháp tạo ra các trường hợp đó, vì vậy ngay cả khi một người sử dụng một công cụ tạo để tạo ra nhiều dữ liệu thử nghiệm, không có gì đảm bảo không bỏ sót một trường hợp thử nghiệm nào có thể phát hiện ra một lỗi nhất định.

Kiểm tra ngẫu nhiên đôi khi có thể tiết lộ một lỗi đã bị bỏ qua bởi các trường hợp kiểm tra được tạo thủ công. Nhưng nói chung, sẽ hiệu quả hơn khi kiểm tra cẩn thận các chức năng cần kiểm tra và đảm bảo rằng người ta có được mã đầy đủ và phạm vi bảo hiểm chi nhánh với càng ít trường hợp kiểm tra càng tốt. Đôi khi nó là một chiến lược khả thi để kết hợp các thử nghiệm được tạo thủ công và ngẫu nhiên. Hơn nữa, khi sử dụng các xét nghiệm ngẫu nhiên, người ta phải cẩn thận để có được kết quả theo cách có thể lặp lại.

Vì vậy, các thử nghiệm được tạo thủ công hoàn toàn không tệ hơn các thử nghiệm được tạo ngẫu nhiên, thường hoàn toàn ngược lại.


1
Bất kỳ bộ kiểm tra thực tế sử dụng kiểm tra ngẫu nhiên cũng sẽ có bài kiểm tra đơn vị. (Về mặt kỹ thuật, các bài kiểm tra đơn vị chỉ là một trường hợp thoái hóa của kiểm tra ngẫu nhiên.) Từ ngữ của bạn cho thấy các bài kiểm tra ngẫu nhiên rất khó đạt được, hoặc việc kết hợp kiểm tra ngẫu nhiên và kiểm tra đơn vị là khó khăn. Điều này thường không phải là trường hợp. Theo tôi, một trong những lợi ích lớn nhất của thử nghiệm ngẫu nhiên là nó khuyến khích mạnh mẽ việc viết thử nghiệm như các thuộc tính về mã được dự định luôn giữ. Tôi muốn có các thuộc tính này được nêu rõ ràng (và được kiểm tra!) Hơn là phải suy ra một số bài kiểm tra điểm.
Derek Elkins rời SE

@DerekElkins: "khó" là IMHO thuật ngữ sai. Các thử nghiệm ngẫu nhiên cần khá nhiều nỗ lực và đó là nỗ lực làm giảm thời gian có sẵn cho các thử nghiệm thủ công (và nếu bạn có những người chỉ tuân theo các khẩu hiệu như câu hỏi được đề cập trong câu hỏi, họ có thể sẽ không làm thủ công chút nào). Chỉ cần ném nhiều dữ liệu thử nghiệm ngẫu nhiên vào một đoạn mã chỉ là một nửa công việc, người ta cũng phải tạo ra kết quả mong đợi cho mỗi đầu vào thử nghiệm đó. Đối với một số kịch bản, điều này có thể được thực hiện tự động. Đối với những người khác, không.
Doc Brown

Mặc dù chắc chắn có những lúc cần một số suy nghĩ để chọn một bản phân phối tốt, nhưng điều này thường không phải là một sự cúp máy lớn. Nhận xét của bạn cho thấy bạn đang nghĩ về điều này sai cách. Các thuộc tính bạn viết để kiểm tra ngẫu nhiên là các thuộc tính giống như bạn sẽ viết để kiểm tra mô hình hoặc cho các bằng chứng chính thức. Thật vậy, chúng có thể và đã được sử dụng cho tất cả những thứ đó cùng một lúc. Không có "kết quả mong đợi" mà bạn cần phải tạo ra. Thay vào đó, bạn chỉ cần nêu một tài sản luôn luôn nên giữ. Một số ví dụ: 1) đẩy thứ gì đó lên ngăn xếp và ...
Derek Elkins rời SE

... Sau đó, popping sẽ giống như không làm gì cả; 2) bất kỳ khách hàng nào có số dư lớn hơn 10.000 đô la sẽ nhận được lãi suất số dư cao và chỉ sau đó; 3) vị trí của sprite luôn nằm trong hộp giới hạn của màn hình. Một số thuộc tính có thể tương ứng với các kiểm tra điểm, ví dụ: "khi số dư là $ 0 đưa ra cảnh báo số dư bằng không". Các thuộc tính là thông số kỹ thuật một phần với lý tưởng là có được một đặc điểm kỹ thuật tổng. Gặp khó khăn khi nghĩ ra các tính chất này có nghĩa là bạn không rõ thông số kỹ thuật là gì và thường có nghĩa là bạn gặp khó khăn khi nghĩ ra các bài kiểm tra đơn vị tốt.
Derek Elkins rời SE

0

Kiểm tra viết bằng tay là "bằng chứng bằng ví dụ". Nhưng QuickCheck cũng vậy, và ở một mức độ giới hạn các hệ thống. Bất cứ điều gì không được xác minh chính thức sẽ bị giới hạn trong những gì nó cho bạn biết về mã của bạn. Thay vào đó, bạn phải suy nghĩ về mặt công đức tương đối của các phương pháp.

Thử nghiệm tạo, như QuickCheck, thực sự tốt cho việc quét một không gian rộng của đầu vào. Nó cũng tốt hơn rất nhiều cho việc giải quyết các trường hợp cạnh so với các bài kiểm tra thủ công: các thư viện kiểm tra tổng quát sẽ có nhiều kinh nghiệm hơn với điều này so với bạn. Mặt khác, họ chỉ cho bạn biết về bất biến, không phải đầu ra cụ thể. Vì vậy, để xác nhận chương trình của bạn đang nhận được kết quả chính xác, bạn vẫn cần một số thử nghiệm thủ công để xác minh rằng, trên thực tế , foo(bar) = baz.

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.