TDD có làm cho chương trình phòng thủ trở nên dư thừa không?


104

Hôm nay tôi đã có một cuộc thảo luận thú vị với một đồng nghiệp.

Tôi là một lập trình viên phòng thủ. Tôi tin rằng quy tắc " một lớp phải đảm bảo rằng các đối tượng của nó có trạng thái hợp lệ khi được tương tác từ bên ngoài lớp " phải luôn được tuân thủ. Lý do cho quy tắc này là lớp không biết người dùng của mình là ai và có thể dự đoán sẽ thất bại khi được tương tác với một cách bất hợp pháp. Theo tôi quy tắc đó áp dụng cho tất cả các lớp.

Trong tình huống cụ thể mà tôi đã thảo luận hôm nay, tôi đã viết mã xác nhận rằng các đối số cho hàm tạo của tôi là chính xác (ví dụ: tham số nguyên phải> 0) và nếu điều kiện tiên quyết không được đáp ứng, thì một ngoại lệ sẽ bị ném. Mặt khác, đồng nghiệp của tôi tin rằng việc kiểm tra như vậy là không cần thiết, bởi vì các bài kiểm tra đơn vị sẽ nắm bắt bất kỳ việc sử dụng không chính xác của lớp. Ngoài ra, ông tin rằng các xác nhận lập trình phòng thủ cũng nên được kiểm tra đơn vị, vì vậy lập trình phòng thủ bổ sung nhiều công việc và do đó không tối ưu cho TDD.

Có đúng là TDD có thể thay thế lập trình phòng thủ? Là xác thực tham số (và tôi không có nghĩa là đầu vào của người dùng) là không cần thiết như là một hệ quả? Hay hai kỹ thuật bổ sung cho nhau?


120
Bạn trao thư viện được kiểm tra đơn vị đầy đủ của bạn mà không có nhà xây dựng kiểm tra cho khách hàng sử dụng và họ phá vỡ hợp đồng lớp. Những gì tốt làm những bài kiểm tra đơn vị làm bạn bây giờ?
Robert Harvey

42
IMO là cách khác. Lập trình phòng thủ, các điều kiện trước và trước phù hợp và một hệ thống loại phong phú làm cho các bài kiểm tra trở nên dư thừa.
vườn

37
Tôi có thể đăng một câu trả lời chỉ nói "Đau buồn không?" Lập trình phòng thủ bảo vệ hệ thống trong thời gian chạy. Các kiểm tra kiểm tra tất cả các điều kiện thời gian chạy tiềm năng mà người kiểm tra có thể nghĩ đến, bao gồm các đối số không hợp lệ được truyền cho các hàm tạo và các phương thức khác. Các kiểm tra, nếu hoàn thành, sẽ xác nhận rằng hành vi thời gian chạy sẽ như mong đợi, bao gồm các ngoại lệ thích hợp được ném hoặc hành vi cố ý khác diễn ra khi các đối số không hợp lệ được thông qua. Nhưng các bài kiểm tra không làm một điều ngu ngốc để bảo vệ hệ thống khi chạy.
Craig

16
"kiểm tra đơn vị nên nắm bắt bất kỳ việc sử dụng không chính xác của lớp" - uh, làm thế nào? Các bài kiểm tra đơn vị sẽ cho bạn thấy hành vi được đưa ra các đối số chính xác và khi đưa ra các đối số không chính xác; họ không thể cho bạn thấy tất cả những lý lẽ mà nó sẽ được đưa ra.
OJFord

34
Tôi không nghĩ rằng tôi đã thấy một ví dụ tốt hơn về cách suy nghĩ giáo điều về phát triển phần mềm có thể dẫn đến kết luận có hại.
sdenham

Câu trả lời:


196

Điều đó thật nực cười. TDD buộc mã phải vượt qua các bài kiểm tra và buộc tất cả các mã phải có một số bài kiểm tra xung quanh nó. Nó không ngăn người tiêu dùng của bạn gọi mã không chính xác, cũng như không ngăn chặn được các lập trình viên thiếu các trường hợp kiểm thử.

Không có phương pháp nào có thể buộc người dùng sử dụng mã chính xác.

một cuộc tranh luận nhẹ phải được thực hiện một cách hoàn hảo rằng nếu bạn đã làm TDD bạn đã có thể bắt séc> 0 của bạn trong một trường hợp thử nghiệm, trước khi thực hiện nó, và giải quyết này - có lẽ bởi bạn thêm tấm séc. Nhưng nếu bạn đã thực hiện TDD, yêu cầu của bạn (> 0 trong hàm tạo) trước tiên sẽ xuất hiện dưới dạng một thử nghiệm thất bại. Do đó cung cấp cho bạn bài kiểm tra sau khi bạn thêm séc của bạn.

Cũng hợp lý khi kiểm tra một số điều kiện phòng thủ (bạn đã thêm logic, tại sao bạn không muốn kiểm tra thứ gì đó dễ kiểm tra đến vậy?). Tôi không chắc tại sao bạn có vẻ không đồng ý với điều này.

Hay hai kỹ thuật bổ sung cho nhau?

TDD sẽ phát triển các bài kiểm tra. Thực hiện xác nhận tham số sẽ làm cho họ vượt qua.


7
Tôi không đồng ý với niềm tin rằng nên kiểm tra điều kiện tiên quyết, nhưng tôi không đồng ý với ý kiến ​​của đồng nghiệp rằng công việc phụ gây ra bởi sự cần thiết phải kiểm tra xác thực điều kiện tiên quyết là một lập luận để không tạo ra xác nhận điều kiện tiên quyết địa điểm. Tôi đã chỉnh sửa bài viết của tôi để làm rõ.
user2180613

20
@ user2180613 Tạo một thử nghiệm kiểm tra sự thất bại của điều kiện tiên quyết được xử lý một cách thích hợp: bây giờ việc thêm kiểm tra không phải là công việc "phụ", đó là công việc mà TDD yêu cầu để làm cho thử nghiệm có màu xanh. Nếu ý kiến ​​của đồng nghiệp của bạn là bạn nên thực hiện bài kiểm tra, quan sát nó không thành công, và sau đó và chỉ sau đó thực hiện kiểm tra điều kiện tiên quyết, thì anh ta có thể có quan điểm từ quan điểm thuần túy TDD. Nếu anh ta nói chỉ cần bỏ qua kiểm tra hoàn toàn, thì anh ta thật ngớ ngẩn. Không có gì trong TDD nói rằng bạn không thể chủ động viết bài kiểm tra cho các chế độ thất bại tiềm năng.
RM

4
@RM Bạn không viết bài kiểm tra để kiểm tra điều kiện tiên quyết. Bạn đang viết một bài kiểm tra để kiểm tra hành vi đúng dự kiến ​​của mã được gọi. Các kiểm tra điều kiện tiên quyết là, từ quan điểm của kiểm tra, một chi tiết triển khai mờ đục đảm bảo hành vi chính xác. Nếu bạn nghĩ ra một cách tốt hơn để đảm bảo đúng trạng thái trong mã được gọi, hãy thực hiện theo cách đó thay vì sử dụng kiểm tra điều kiện tiên quyết truyền thống. Bài kiểm tra sẽ cho biết bạn có thành công hay không và vẫn không biết hoặc quan tâm bạn đã làm như thế nào .
Craig

@ user2180613 Đó là một lời biện minh tuyệt vời: D nếu mục tiêu của bạn trong việc viết phần mềm là bạn giảm số lượng bài kiểm tra bạn cần để tác giả và chạy, đừng viết bất kỳ phần mềm nào - không có bài kiểm tra nào!
Gusdor

3
Đó là câu cuối cùng của câu trả lời này.
Robert Grant

32

Lập trình phòng thủ và kiểm tra đơn vị là hai cách khác nhau để bắt lỗi và mỗi cách có điểm mạnh khác nhau. Chỉ sử dụng một cách phát hiện lỗi khiến cơ chế phát hiện lỗi của bạn trở nên mong manh. Sử dụng cả hai sẽ bắt được các lỗi có thể bị bỏ qua bởi người này hoặc người kia, ngay cả trong mã không phải là API phải đối mặt công khai; ví dụ, ai đó có thể đã quên thêm một bài kiểm tra đơn vị cho dữ liệu không hợp lệ được truyền vào API công khai. Kiểm tra mọi thứ ở những nơi thích hợp có nghĩa là có nhiều cơ hội hơn để bắt lỗi.

Trong bảo mật thông tin, điều này được gọi là Defense In Depth. Có nhiều lớp phòng thủ đảm bảo rằng nếu một lần thất bại, vẫn còn những lớp khác để bắt nó.

Đồng nghiệp của bạn nói đúng về một điều: Bạn nên kiểm tra xác nhận của mình, nhưng đây không phải là "công việc không cần thiết". Nó cũng giống như thử nghiệm bất kỳ mã nào khác, bạn muốn đảm bảo tất cả các cách sử dụng, ngay cả những mã không hợp lệ, đều có kết quả như mong đợi.


Có đúng không khi nói rằng xác nhận tham số là một hình thức xác thực tiền điều kiện và kiểm tra đơn vị là xác nhận hậu điều kiện, đó là lý do tại sao chúng bổ sung cho nhau?
dùng2180613

1
"Nó cũng giống như thử nghiệm bất kỳ mã nào khác, bạn muốn đảm bảo tất cả các cách sử dụng, ngay cả những mã không hợp lệ, đều có kết quả như mong đợi." Điều này. Không có mã nào được chuyển qua khi đầu vào được chuyển qua, nó không được thiết kế để xử lý. Điều này vi phạm nguyên tắc "thất bại nhanh" và nó có thể khiến việc gỡ lỗi trở thành một cơn ác mộng.
jpmc26

@ user2180613 - không thực sự, nhưng hơn thế nữa, các bài kiểm tra đơn vị kiểm tra các điều kiện thất bại mà nhà phát triển đang mong đợi, trong khi các kỹ thuật lập trình phòng thủ kiểm tra các điều kiện mà nhà phát triển không mong đợi. Các thử nghiệm đơn vị có thể được sử dụng để xác nhận các điều kiện tiên quyết (bằng cách sử dụng một đối tượng giả được tiêm vào người gọi để kiểm tra điều kiện tiên quyết).
Periata Breatta

1
@ jpmc26 Có, thất bại kết quả mong đợi của người dùng cho thử nghiệm. Bạn kiểm tra để cho thấy rằng nó thất bại, thay vì âm thầm thể hiện một số hành vi không xác định (không mong muốn).
KRyan

6
TDD bắt lỗi trong mã của riêng bạn, lập trình phòng thủ bắt lỗi trong mã của người khác. TDD do đó có thể giúp đảm bảo rằng bạn đủ :) phòng thủ
jwenting

30

TDD hoàn toàn không thay thế chương trình phòng thủ. Thay vào đó, bạn có thể sử dụng TDD để đảm bảo tất cả các phòng thủ được đặt đúng chỗ và hoạt động như mong đợi.

Trong TDD, bạn không được phép viết mã mà không viết bài kiểm tra trước - hãy thực hiện theo chu trình tái cấu trúc màu xanh lá cây màu đỏ. Điều đó có nghĩa là nếu bạn muốn thêm xác nhận, trước tiên hãy viết một bài kiểm tra yêu cầu xác thực này. Gọi phương thức được đề cập với các số âm và bằng 0 và hy vọng nó sẽ đưa ra một ngoại lệ.

Ngoài ra, đừng quên bước tái cấu trúc của người Viking. Trong khi TDD được điều khiển thử nghiệm , điều này không có nghĩa là chỉ thử nghiệm . Bạn vẫn nên áp dụng thiết kế phù hợp và viết mã hợp lý. Viết mã phòng thủ là mã hợp lý, bởi vì nó làm cho kỳ vọng rõ ràng hơn và mã của bạn nói chung mạnh mẽ hơn - phát hiện sớm các lỗi có thể khiến chúng dễ gỡ lỗi hơn.

Nhưng chúng ta không nên sử dụng các bài kiểm tra để xác định vị trí lỗi? Khẳng định và kiểm tra là bổ sung. Một chiến lược thử nghiệm tốt sẽ kết hợp nhiều cách tiếp cận khác nhau để đảm bảo phần mềm mạnh mẽ. Chỉ kiểm thử đơn vị hoặc chỉ kiểm thử tích hợp hoặc chỉ xác nhận trong mã là không thỏa đáng, bạn cần một sự kết hợp tốt để đạt được mức độ tin cậy đủ đối với phần mềm của bạn với nỗ lực chấp nhận được.

Sau đó, có một sự hiểu lầm rất lớn về khái niệm của đồng nghiệp của bạn: Các bài kiểm tra đơn vị không bao giờ có thể kiểm tra việc sử dụng lớp của bạn, chỉ có điều chính lớp đó hoạt động như mong đợi trong sự cô lập. Bạn sẽ sử dụng các thử nghiệm tích hợp để kiểm tra xem sự tương tác giữa các thành phần khác nhau có hoạt động hay không, nhưng sự bùng nổ kết hợp của các trường hợp thử nghiệm có thể khiến cho không thể kiểm tra mọi thứ. Do đó, kiểm tra tích hợp nên hạn chế một vài trường hợp quan trọng. Các thử nghiệm chi tiết hơn cũng bao gồm các trường hợp cạnh và các trường hợp lỗi phù hợp hơn cho các thử nghiệm đơn vị.


16

Các thử nghiệm ở đó để hỗ trợ và đảm bảo lập trình phòng thủ

Lập trình phòng thủ bảo vệ tính toàn vẹn của hệ thống khi chạy.

Các xét nghiệm là các công cụ chẩn đoán (chủ yếu là tĩnh). Trong thời gian chạy, các bài kiểm tra của bạn không ở đâu trong tầm nhìn. Chúng giống như giàn giáo được sử dụng để dựng lên một bức tường gạch cao hoặc một mái vòm bằng đá. Bạn không để các bộ phận quan trọng ra khỏi cấu trúc vì bạn có một giàn giáo giữ nó trong khi xây dựng. Bạn có một giàn giáo giữ nó trong khi xây dựng để tạo điều kiện cho tất cả các phần quan trọng vào.

EDIT: Một sự tương tự

Điều gì về một sự tương tự với ý kiến ​​trong mã?

Nhận xét có mục đích của họ, nhưng có thể là dư thừa hoặc thậm chí có hại. Ví dụ: nếu bạn đưa kiến ​​thức nội tại về mã vào các bình luận , sau đó thay đổi mã, các bình luận trở nên không liên quan ở mức tốt nhất và có hại ở mức tồi tệ nhất.

Vì vậy, giả sử bạn đưa nhiều kiến ​​thức nội tại về cơ sở mã của mình vào các bài kiểm tra, chẳng hạn như Phương thứcA không thể lấy giá trị và đối số của Phương thức phải có > 0. Sau đó, mã thay đổi. Null hiện đã ổn cho A và B có thể lấy các giá trị nhỏ bằng -10. Các xét nghiệm hiện tại đã sai chức năng, nhưng sẽ tiếp tục vượt qua.

Có, bạn nên cập nhật các bài kiểm tra cùng lúc bạn cập nhật mã. Bạn cũng nên cập nhật (hoặc xóa) các bình luận cùng lúc bạn cập nhật mã. Nhưng tất cả chúng ta đều biết những điều này không phải lúc nào cũng xảy ra, và những sai lầm đó đã được tạo ra.

Các xét nghiệm xác minh hành vi của hệ thống. Hành vi thực tế đó là nội tại đối với chính hệ thống, không phải nội tại đối với các thử nghiệm.

Cái gì có thể đi sai?

Mục tiêu liên quan đến các bài kiểm tra là nghĩ ra mọi thứ có thể sai, viết một bài kiểm tra cho nó để kiểm tra hành vi đúng, sau đó tạo mã thời gian chạy để nó vượt qua tất cả các bài kiểm tra.

Điều đó có nghĩa là lập trình phòng thủ là điểm chính .

TDD thúc đẩy lập trình phòng thủ, nếu các bài kiểm tra là toàn diện.

Nhiều bài kiểm tra, lái xe lập trình nhiều hơn

Khi tìm thấy lỗi chắc chắn, nhiều bài kiểm tra được viết để mô hình hóa các điều kiện biểu hiện lỗi. Sau đó, mã được sửa, với mã để thực hiện các thử nghiệm đó và các thử nghiệm mới vẫn nằm trong bộ thử nghiệm.

Một tập hợp các bài kiểm tra tốt sẽ chuyển cả đối số tốt và xấu cho hàm / phương thức và mong đợi kết quả nhất quán. Điều này, đến lượt nó, có nghĩa là thành phần được thử nghiệm sẽ sử dụng kiểm tra điều kiện tiên quyết (lập trình phòng thủ) để xác nhận các đối số được truyền cho nó.

Nói chung ...

Ví dụ: nếu một đối số null cho một thủ tục cụ thể là không hợp lệ, thì ít nhất một thử nghiệm sẽ vượt qua null và nó sẽ xuất hiện một ngoại lệ / lỗi "đối số null không hợp lệ".

Tất nhiên, ít nhất một thử nghiệm khác sẽ vượt qua một đối số hợp lệ - hoặc lặp qua một mảng lớn và vượt qua nhiều đối số hợp lệ - và xác nhận rằng trạng thái kết quả là phù hợp.

Nếu một bài kiểm tra không vượt qua được đối số null đó và bị tát với ngoại lệ dự kiến ​​(và ngoại lệ đó đã bị ném vì mã kiểm tra chắc chắn trạng thái được truyền cho nó), thì null có thể được gán cho một thuộc tính của một lớp hoặc bị chôn vùi trong một bộ sưu tập của một số nơi mà nó không nên

Điều này có thể gây ra hành vi không mong muốn trong một số phần hoàn toàn khác của hệ thống mà cá thể lớp được truyền tới, ở một số địa điểm địa lý xa sau khi phần mềm đã xuất xưởng . Và đó là thứ chúng ta thực sự đang cố tránh, phải không?

Nó thậm chí có thể tồi tệ hơn. Ví dụ lớp với trạng thái không hợp lệ có thể được tuần tự hóa và được lưu trữ, chỉ gây ra lỗi khi nó được hoàn nguyên để sử dụng sau này. Geez, tôi không biết, có lẽ đó là một hệ thống điều khiển cơ học thuộc loại nào đó không thể khởi động lại sau khi tắt máy vì nó không thể khử lưu lượng trạng thái cấu hình liên tục của chính nó. Hoặc cá thể lớp có thể được tuần tự hóa và được chuyển đến một số hệ thống hoàn toàn khác được tạo bởi một thực thể khác và hệ thống đó có thể bị sập.

Đặc biệt là nếu các lập trình viên của hệ thống khác không viết mã phòng thủ.


2
Điều đó thật buồn cười, downvote đến quá nhanh, hoàn toàn có thể đọc được đoạn dưới đầu tiên.
Craig

1
:-) Tôi chỉ bình chọn mà không đọc vượt quá đoạn đầu tiên, vì vậy hy vọng điều đó sẽ cân bằng nó ...
SusanW

1
Dường như ít nhất tôi có thể làm :-) (Trên thực tế, tôi đã đọc phần còn lại chỉ để đảm bảo. Không được cẩu thả - đặc biệt là về một chủ đề như thế này!)
SusanW

1
Tôi hình dung bạn có thể có. :)
Craig

kiểm tra phòng thủ có thể được thực hiện tại thời điểm biên dịch với các công cụ như Hợp đồng mã.
Matthew Whited

9

Thay vì TDD, hãy nói về "kiểm thử phần mềm" nói chung và thay vì "lập trình phòng thủ" nói chung, hãy nói về cách yêu thích của tôi khi lập trình phòng thủ, đó là bằng cách sử dụng các xác nhận.


Vì vậy, vì chúng tôi kiểm thử phần mềm, chúng tôi nên bỏ việc đặt các câu lệnh khẳng định trong mã sản xuất, phải không? Hãy để tôi đếm những cách mà điều này là sai:

  1. Các xác nhận là tùy chọn, vì vậy nếu bạn không thích chúng, chỉ cần chạy hệ thống của bạn với các xác nhận bị vô hiệu hóa.

  2. Các xác nhận kiểm tra những thứ mà kiểm tra không thể (và không nên.) Bởi vì kiểm tra được cho là có chế độ xem hộp đen của hệ thống của bạn, trong khi các xác nhận có chế độ xem hộp trắng. (Tất nhiên, vì họ sống trong đó.)

  3. Khẳng định là một công cụ tài liệu tuyệt vời. Không có bình luận nào đã từng, hoặc sẽ không rõ ràng như một đoạn mã khẳng định điều tương tự. Ngoài ra, tài liệu có xu hướng trở nên lỗi thời khi mã phát triển, và nó không bị ép buộc bởi trình biên dịch.

  4. Các xác nhận có thể bắt lỗi trong mã kiểm tra. Bạn đã bao giờ gặp phải tình huống thử nghiệm thất bại và bạn không biết ai sai - mã sản xuất hay thử nghiệm?

  5. Các xác nhận có thể thích hợp hơn so với thử nghiệm. Các thử nghiệm sẽ kiểm tra những gì được quy định bởi các yêu cầu chức năng, nhưng mã thường phải đưa ra một số giả định nhất định mang tính kỹ thuật cao hơn thế. Những người viết các tài liệu yêu cầu chức năng hiếm khi nghĩ đến việc chia cho số không.

  6. Khẳng định xác định chính xác các lỗi mà chỉ kiểm tra gợi ý rộng rãi tại. Vì vậy, thử nghiệm của bạn thiết lập một số điều kiện tiên quyết mở rộng, gọi một số đoạn mã dài, thu thập kết quả và thấy rằng chúng không như mong đợi. Đưa ra đủ cách khắc phục sự cố, cuối cùng bạn sẽ tìm thấy chính xác nơi xảy ra sự cố, nhưng các xác nhận thường sẽ tìm thấy trước.

  7. Khẳng định giảm độ phức tạp của chương trình. Mỗi dòng mã mà bạn viết sẽ làm tăng độ phức tạp của chương trình. Các xác nhận và từ khóa final( readonly) là hai cấu trúc duy nhất mà tôi biết về điều đó thực sự làm giảm độ phức tạp của chương trình. Đó là vô giá.

  8. Các xác nhận giúp trình biên dịch hiểu rõ hơn về mã của bạn. Vui lòng thử điều này ở nhà: void foo( Object x ) { assert x != null; if( x == null ) { } }trình biên dịch của bạn sẽ đưa ra cảnh báo cho bạn biết rằng điều kiện x == nullnày luôn luôn sai. Điều đó có thể rất hữu ích.

Trên đây là tóm tắt một bài đăng từ blog của tôi, 2014-09-21 "Xác nhận và thử nghiệm"


Tôi nghĩ rằng tôi chủ yếu không đồng ý với câu trả lời này. (5) Trong TDD, bộ kiểm tra là đặc điểm kỹ thuật. Bạn phải viết mã đơn giản nhất để thực hiện các bài kiểm tra, không có gì hơn. (4) Luồng công việc màu xanh lá cây màu đỏ đảm bảo rằng thử nghiệm thất bại khi cần và vượt qua khi có chức năng dự định. Khẳng định không giúp được gì nhiều ở đây. (3,7) Tài liệu là tài liệu, khẳng định thì không. Nhưng bằng cách làm cho các giả định rõ ràng, mã trở nên tự viết tài liệu hơn. Tôi nghĩ về họ như những bình luận thực thi. (2) Thử nghiệm hộp trắng có thể là một phần của chiến lược thử nghiệm hợp lệ.
amon

5
"Trong TDD, bộ kiểm thử là đặc điểm kỹ thuật. Bạn nên viết mã đơn giản nhất để kiểm tra vượt qua, không có gì nữa.": Tôi không nghĩ rằng đây luôn là một ý tưởng hay: Như đã chỉ ra trong câu trả lời, có giả định nội bộ bổ sung trong mã mà người ta có thể muốn xác minh. Điều gì về các lỗi nội bộ triệt tiêu lẫn nhau? Các thử nghiệm của bạn vượt qua nhưng một vài giả định bên trong mã của bạn là sai, điều này có thể dẫn đến các lỗi sâu sắc sau này.
Giorgio

5

Tôi tin rằng hầu hết các câu trả lời đều thiếu một sự khác biệt quan trọng: Nó phụ thuộc vào cách mã của bạn sẽ được sử dụng.

Là mô-đun trong câu hỏi sẽ được sử dụng bởi các khách hàng khác độc lập của ứng dụng bạn đang thử nghiệm? Nếu bạn đang cung cấp thư viện hoặc API để bên thứ ba sử dụng, bạn không có cách nào đảm bảo họ chỉ gọi mã của bạn bằng đầu vào hợp lệ. Bạn phải xác nhận tất cả các đầu vào.

Nhưng nếu mô-đun trong câu hỏi chỉ được sử dụng bởi mã bạn kiểm soát, thì bạn của bạn có thể có một điểm. Bạn có thể sử dụng các bài kiểm tra đơn vị để xác minh rằng mô-đun được đề cập chỉ được gọi với đầu vào hợp lệ. Kiểm tra điều kiện tiên quyết vẫn có thể được coi là một thông lệ tốt, nhưng đó là một sự đánh đổi: Tôi bạn xả rác mã kiểm tra tình trạng mà bạn biết không bao giờ có thể phát sinh, nó chỉ che khuất ý định của mã.

Tôi không đồng ý rằng kiểm tra điều kiện tiên quyết đòi hỏi nhiều bài kiểm tra đơn vị hơn. Nếu bạn quyết định bạn không cần kiểm tra một số dạng đầu vào không hợp lệ, thì việc này có nên kiểm tra điều kiện tiên quyết hay không. Hãy nhớ kiểm tra nên xác minh hành vi, không thực hiện chi tiết.


4
Nếu thủ tục được gọi không xác minh tính hợp lệ của các đầu vào (là cuộc tranh luận ban đầu) thì các kiểm tra đơn vị của bạn không thể đảm bảo rằng mô-đun được đề cập chỉ được gọi với đầu vào hợp lệ. Cụ thể, nó có thể được gọi với đầu vào không hợp lệ nhưng dù sao cũng chỉ trả về kết quả chính xác trong các trường hợp được kiểm tra - có nhiều loại hành vi không xác định, xử lý tràn, v.v ... có thể trả về kết quả mong đợi trong môi trường thử nghiệm với tối ưu hóa bị vô hiệu hóa nhưng thất bại trong sản xuất.
Peteris

@Peteris: Bạn đang nghĩ về hành vi không xác định như trong C? Gọi hành vi không xác định có kết quả khác nhau trong các môi trường khác nhau rõ ràng là một lỗi, nhưng nó cũng không thể được ngăn chặn bằng các kiểm tra tiền điều kiện. Ví dụ, làm thế nào để bạn kiểm tra một đối số con trỏ trỏ đến bộ nhớ hợp lệ?
JacquesB

3
Điều này sẽ chỉ làm việc trong các cửa hàng nhỏ nhất. Khi nhóm của bạn vượt ra ngoài, giả sử, sáu người, dù sao bạn cũng sẽ cần kiểm tra xác thực.
Robert Harvey

1
@RobertHarvey: Trong trường hợp đó, hệ thống nên được phân vùng thành các hệ thống con với giao diện được xác định rõ và xác thực đầu vào được thực hiện tại giao diện.
JacquesB

điều này. Nó phụ thuộc vào mã, mã này có được sử dụng bởi nhóm không? Đội có quyền truy cập vào mã nguồn? Nếu mã hoàn toàn bên trong của nó thì việc kiểm tra các đối số có thể chỉ là một gánh nặng, ví dụ, bạn kiểm tra 0 rồi ném ngoại lệ và người gọi sẽ nhìn vào mã oh lớp này có thể ném ngoại lệ, v.v. và chờ đợi .. trong trường hợp này đối tượng sẽ không bao giờ nhận được 0 vì chúng được lọc ra 2 lvls trước đó. Nếu đó là mã thư viện được sử dụng bởi các bên thứ 3 thì đó là một câu chuyện khác. Không có tất cả các mã được viết để được sử dụng bởi cả thế giới.
Aleksander Fular

3

Đối số này gây khó khăn cho tôi, bởi vì khi tôi bắt đầu thực hành TDD, đơn vị của tôi kiểm tra mẫu "đối tượng phản hồi <cách nhất định> khi <đầu vào không hợp lệ>" tăng 2 hoặc 3 lần. Tôi đang tự hỏi làm thế nào đồng nghiệp của bạn đang quản lý để vượt qua thành công các loại bài kiểm tra đơn vị đó mà không có chức năng của anh ấy thực hiện xác nhận.

Trường hợp ngược lại, các bài kiểm tra đơn vị cho thấy bạn sẽ không bao giờ tạo ra kết quả đầu ra xấu sẽ được chuyển cho các đối số của các hàm khác, khó chứng minh hơn nhiều. Giống như trường hợp đầu tiên, nó phụ thuộc rất nhiều vào độ bao phủ toàn diện của các trường hợp cạnh, nhưng bạn có yêu cầu bổ sung rằng tất cả các đầu vào chức năng của bạn phải đến từ đầu ra của các chức năng khác có đầu ra mà bạn đã kiểm tra và không phải từ đầu vào của người dùng hoặc mô-đun bên thứ ba.

Nói cách khác, những gì TDD không ngăn bạn cần mã xác nhận cũng như giúp bạn tránh quên nó.


2

Tôi nghĩ rằng tôi diễn giải nhận xét của đồng nghiệp của bạn khác với hầu hết các câu trả lời còn lại.

Dường như với tôi rằng đối số là:

  • Tất cả các mã của chúng tôi là đơn vị thử nghiệm.
  • Tất cả mã sử dụng thành phần của bạn là mã của chúng tôi hoặc nếu không phải là đơn vị được kiểm tra bởi người khác (không được nêu rõ ràng, nhưng đó là những gì tôi hiểu từ "kiểm tra đơn vị nên nắm bắt bất kỳ cách sử dụng sai nào của lớp").
  • Do đó, đối với mỗi người gọi chức năng của bạn, có một bài kiểm tra đơn vị ở đâu đó giả định thành phần của bạn và kiểm tra không thành công nếu người gọi chuyển một giá trị không hợp lệ cho giả định đó.
  • Do đó, chức năng của bạn không quan trọng khi thông qua một giá trị không hợp lệ, bởi vì các thử nghiệm của chúng tôi nói rằng điều đó không thể xảy ra.

Đối với tôi, lập luận này có một số logic với nó, nhưng đặt quá nhiều sự phụ thuộc vào các bài kiểm tra đơn vị để bao quát mọi tình huống có thể xảy ra. Một thực tế đơn giản là phạm vi bảo hiểm 100% đường / nhánh / đường dẫn không nhất thiết phải thực hiện mọi giá trị mà người gọi có thể vượt qua, trong khi phạm vi bảo hiểm 100% của tất cả các trạng thái có thể có của người gọi (nghĩa là tất cả các giá trị có thể có của đầu vào và các biến) là không thể tính toán được.

Do đó, tôi có xu hướng thích kiểm tra đơn vị người gọi để đảm bảo rằng (cho đến khi các cuộc kiểm tra diễn ra) họ không bao giờ vượt qua các giá trị xấu và ngoài ra, yêu cầu thành phần của bạn thất bại theo cách dễ nhận biết khi giá trị xấu được truyền vào ( ít nhất là có thể nhận ra các giá trị xấu trong ngôn ngữ bạn chọn). Điều này sẽ hỗ trợ gỡ lỗi khi xảy ra sự cố trong các thử nghiệm tích hợp và cũng sẽ hỗ trợ bất kỳ người dùng nào trong lớp của bạn, những người không nghiêm ngặt trong việc cách ly đơn vị mã của họ khỏi sự phụ thuộc đó.

Mặc dù vậy, hãy lưu ý rằng nếu bạn ghi lại và kiểm tra hành vi của hàm khi giá trị <= 0 được truyền vào thì các giá trị âm không còn hợp lệ nữa (ít nhất, không có giá trị nào hơn bất kỳ đối số nào cả throw, vì điều đó quá là tài liệu để ném một ngoại lệ!). Người gọi được quyền dựa vào hành vi phòng thủ đó. Ngôn ngữ cho phép, nó có thể rằng đây là trong mọi trường hợp kịch bản tốt nhất - chức năng không có "đầu vào không hợp lệ", nhưng những người gọi người mong đợi không khiêu khích các chức năng vào ném một ngoại lệ nên đơn vị được kiểm tra đầy đủ để đảm bảo họ không' t vượt qua bất kỳ giá trị nào gây ra điều đó.

Mặc dù nghĩ rằng đồng nghiệp của bạn hơi sai hoàn toàn so với hầu hết các câu trả lời, tôi đi đến cùng một kết luận, đó là hai kỹ thuật bổ sung cho nhau. Chương trình phòng thủ, tài liệu kiểm tra phòng thủ của bạn, và kiểm tra chúng. Công việc chỉ "không cần thiết" nếu người dùng mã của bạn không thể hưởng lợi từ các thông báo lỗi hữu ích khi họ mắc lỗi. Về lý thuyết, nếu họ kiểm tra kỹ lưỡng tất cả mã của họ trước khi tích hợp nó với mã của bạn và không bao giờ có bất kỳ lỗi nào trong các thử nghiệm của họ, thì họ sẽ không bao giờ thấy các thông báo lỗi. Trong thực tế ngay cả khi họ đang thực hiện TDD và tiêm phụ thuộc hoàn toàn, họ vẫn có thể khám phá trong quá trình phát triển hoặc có thể có một sai sót trong thử nghiệm của họ. Kết quả là họ gọi mã của bạn trước khi mã của họ hoàn hảo!


Công việc tập trung vào việc kiểm tra người gọi để đảm bảo rằng họ không vượt qua các giá trị xấu dường như tự cho mình mượn mã dễ vỡ với nhiều phụ thuộc âm trầm, và không có sự phân tách rõ ràng. Tôi thực sự không nghĩ rằng tôi thích mã sẽ xuất phát từ suy nghĩ đằng sau cách tiếp cận đó.
Craig

@Craig: nhìn vào nó theo cách này, nếu bạn đã tách một thành phần để kiểm tra bằng cách chế nhạo các phụ thuộc của nó, thì tại sao bạn không kiểm tra rằng nó chỉ chuyển các giá trị đúng cho các phụ thuộc đó? Và nếu bạn không thể cô lập thành phần, bạn có thực sự tách biệt mối quan tâm? Tôi không đồng ý với mã hóa phòng thủ, nhưng nếu kiểm tra phòng thủ là phương tiện để bạn kiểm tra tính chính xác của mã cuộc gọi thì đó là một mớ hỗn độn. Vì vậy, tôi nghĩ rằng đồng nghiệp của người hỏi là chính xác rằng séc là dư thừa, nhưng sai khi xem đây là lý do để không viết chúng :-)
Steve Jessop

lỗ hổng duy nhất mà tôi thấy là tôi vẫn chỉ kiểm tra các thành phần của chính mình không thể chuyển các giá trị không hợp lệ cho các phụ thuộc đó, điều mà tôi hoàn toàn đồng ý nên thực hiện, nhưng có bao nhiêu quyết định của bao nhiêu người quản lý doanh nghiệp để đặt riêng tư thành phần công cộng để các đối tác có thể gọi nó? Điều này thực sự nhắc nhở tôi về thiết kế cơ sở dữ liệu và tất cả các mối tình hiện tại với ORM, dẫn đến rất nhiều người (chủ yếu là trẻ tuổi) tuyên bố rằng cơ sở dữ liệu chỉ là lưu trữ mạng câm và không nên tự bảo vệ mình bằng các ràng buộc, khóa ngoại và thủ tục lưu trữ.
Craig

Một điều khác tôi thấy là trong kịch bản đó, tất nhiên, là bạn chỉ kiểm tra các cuộc gọi đến giả, không phải cho các phụ thuộc thực tế. Cuối cùng, đó là mã trong các phụ thuộc có thể hoặc không thể hoạt động phù hợp với một giá trị được truyền cụ thể, không phải mã trong trình gọi. Vì vậy, sự phụ thuộc cần phải làm đúng, và cần có đủ phạm vi kiểm tra độc lập của sự phụ thuộc để đảm bảo rằng nó làm được. Hãy nhớ rằng, những bài kiểm tra mà chúng tôi đang nói đến được gọi là bài kiểm tra "đơn vị". Mỗi phụ thuộc là một đơn vị. :)
Craig

1

Giao diện công cộng có thể và sẽ bị lạm dụng

Khiếu nại của đồng nghiệp của bạn "các bài kiểm tra đơn vị sẽ nắm bắt mọi sử dụng không chính xác của lớp" là hoàn toàn sai đối với bất kỳ giao diện nào không riêng tư. Nếu một hàm công khai có thể được gọi với các đối số nguyên, thì nó có thể và sẽ được gọi với bất kỳ đối số nguyên nào và mã sẽ hành xử phù hợp. Nếu chữ ký hàm công khai chấp nhận loại Java Double, thì null, NaN, MAX_VALUE, -Inf đều là các giá trị có thể. Các bài kiểm tra đơn vị của bạn không thể bắt được việc sử dụng lớp không chính xác vì các bài kiểm tra đó không thể kiểm tra mã sẽ sử dụng lớp này, vì mã đó chưa được viết, có thể không được bạn viết và chắc chắn sẽ nằm ngoài phạm vi kiểm tra đơn vị của bạn .

Mặt khác, cách tiếp cận này có thể hợp lệ đối với các thuộc tính riêng tư (hy vọng nhiều hơn rất nhiều) - nếu một lớp có thể đảm bảo rằng một số thực tế luôn luôn đúng (ví dụ: thuộc tính X không bao giờ có thể là null, vị trí số nguyên không vượt quá độ dài tối đa , khi hàm A được gọi, tất cả các cấu trúc dữ liệu tiên quyết được hình thành tốt) thì có thể thích hợp để tránh xác minh điều này nhiều lần vì lý do hiệu suất và thay vào đó dựa vào kiểm tra đơn vị.


Tiêu đề và đoạn đầu tiên của điều này là đúng bởi vì đây không phải là bài kiểm tra đơn vị sẽ thực thi mã khi chạy. Đó là bất kỳ mã thời gian chạy nào khác và thay đổi điều kiện trong thế giới thực và đầu vào người dùng xấu và các nỗ lực hack tương tác với mã.
Craig

1

Bảo vệ chống lạm dụng là một tính năng , được phát triển do một yêu cầu cho nó. (Không phải tất cả các giao diện đều yêu cầu kiểm tra nghiêm ngặt chống lại việc sử dụng sai; ví dụ: các giao diện nội bộ được sử dụng rất hẹp.)

Tính năng này yêu cầu thử nghiệm: bảo vệ chống lạm dụng có thực sự hoạt động không? Mục tiêu của việc kiểm tra tính năng này là cố gắng chứng minh rằng nó không: để sử dụng sai một số mô-đun không bị kiểm tra.

Nếu kiểm tra cụ thể là một tính năng bắt buộc, thì thực sự vô lý khi khẳng định rằng sự tồn tại của một số thử nghiệm khiến chúng không cần thiết. Nếu nó là một tính năng của một số chức năng (giả sử), nó đưa ra một ngoại lệ khi tham số ba là âm, thì điều đó không thể thương lượng được; nó sẽ làm điều đó

Tuy nhiên, tôi nghi ngờ rằng đồng nghiệp của bạn thực sự có ý nghĩa từ quan điểm của một tình huống trong đó không có yêu cầu kiểm tra cụ thể về đầu vào, với các phản ứng cụ thể đối với đầu vào xấu: một tình huống chỉ có một yêu cầu chung được hiểu đối với sự mạnh mẽ.

Kiểm tra khi vào một số chức năng cấp cao nhất, một phần, để bảo vệ một số mã nội bộ được kiểm tra yếu hoặc kém khỏi các kết hợp tham số không mong muốn (như nếu mã được kiểm tra tốt, kiểm tra là không cần thiết: mã chỉ có thể " thời tiết "các thông số xấu).

Có một sự thật trong ý tưởng của đồng nghiệp, và ý nghĩa của anh ta là: nếu chúng ta xây dựng một chức năng từ các phần cấp thấp rất mạnh mẽ được mã hóa và kiểm tra riêng lẻ chống lại tất cả các hành vi sử dụng sai, thì có thể chức năng cấp cao hơn có thể mạnh mẽ mà không cần tự kiểm tra rộng rãi.

Nếu hợp đồng của nó bị vi phạm, thì nó sẽ chuyển sang sử dụng sai một số chức năng cấp thấp hơn, có thể bằng cách ném ngoại lệ hoặc bất cứ điều gì.

Vấn đề duy nhất là các ngoại lệ cấp thấp hơn không dành riêng cho giao diện cấp cao hơn. Cho dù đó là một vấn đề phụ thuộc vào các yêu cầu là gì. Nếu yêu cầu chỉ đơn giản là "chức năng sẽ mạnh mẽ chống lại việc sử dụng sai và ném một loại ngoại lệ nào đó chứ không phải là sự cố, hoặc tiếp tục tính toán với dữ liệu rác" thì thực tế nó có thể được bao phủ bởi tất cả sự mạnh mẽ của các phần cấp thấp hơn. được xây dựng.

Nếu hàm có yêu cầu báo cáo lỗi chi tiết, cụ thể liên quan đến các tham số của nó, thì kiểm tra mức thấp hơn không đáp ứng đầy đủ các yêu cầu đó. Họ chỉ đảm bảo rằng chức năng này sẽ nổ tung bằng cách nào đó (không tiếp tục với sự kết hợp xấu của các tham số, tạo ra kết quả rác). Nếu mã máy khách được viết để bắt lỗi cụ thể và xử lý chúng, nó có thể không hoạt động chính xác. Mã khách hàng có thể tự lấy, làm đầu vào, dữ liệu dựa trên các tham số và có thể mong đợi hàm kiểm tra các giá trị này và chuyển các giá trị xấu sang các lỗi cụ thể như được ghi lại (để nó có thể xử lý các lỗi đó lỗi chính xác) thay vì một số lỗi khác không được xử lý và có thể dừng hình ảnh phần mềm.

TL; DR: đồng nghiệp của bạn có lẽ không phải là một thằng ngốc; bạn chỉ đang nói chuyện với nhau với những quan điểm khác nhau xung quanh cùng một vấn đề, bởi vì các yêu cầu không được đóng đinh hoàn toàn và mỗi bạn có một ý tưởng khác nhau về "yêu cầu bất thành văn" là gì. Bạn nghĩ rằng khi không có yêu cầu cụ thể về kiểm tra tham số, bạn vẫn nên mã hóa kiểm tra chi tiết; đồng nghiệp nghĩ, chỉ cần để mã cấp thấp mạnh mẽ hơn nổ tung khi các tham số sai. Việc tranh luận về các yêu cầu không được cung cấp thông qua mã là hơi không hiệu quả: nhận ra rằng bạn không đồng ý về các yêu cầu hơn là mã. Cách mã hóa của bạn phản ánh những gì bạn nghĩ các yêu cầu là; cách của đồng nghiệp thể hiện quan điểm của anh ấy về các yêu cầu. Nếu bạn thấy nó theo cách đó, rõ ràng điều gì đúng hay sai không phải là ' t trong mã chính nó; mã chỉ là một proxy cho ý kiến ​​của bạn về đặc điểm kỹ thuật nên là gì.


Điều này liên quan đến một khó khăn triết học chung trong việc xử lý những gì có thể là yêu cầu lỏng lẻo. Nếu một chức năng được cho phép đáng kể nhưng không hoàn toàn tự do hành xử tùy ý khi đưa ra các đầu vào không đúng định dạng (ví dụ: nếu bộ giải mã hình ảnh có thể đáp ứng các yêu cầu nếu có thể được đảm bảo - khi rảnh rỗi - có thể tạo ra một số kết hợp pixel tùy ý hoặc chấm dứt bất thường , nhưng không phải nếu nó có thể cho phép các đầu vào được chế tạo độc hại thực thi mã tùy ý), có thể không rõ trường hợp thử nghiệm nào là phù hợp để đảm bảo rằng không có đầu vào nào tạo ra hành vi không chấp nhận được.
supercat

1

Các xét nghiệm xác định hợp đồng của lớp bạn.

Như một hệ quả tất yếu, sự vắng mặt của một bài kiểm tra xác định một hợp đồng bao gồm hành vi không xác định . Vì vậy, khi bạn vượt qua nullđể Foo::Frobnicate(Widget widget), và không kể xiết thời gian chạy tàn phá xảy ra sau đó, bạn vẫn nằm trong hợp đồng của lớp học của bạn.

Sau đó, bạn quyết định, "chúng tôi không muốn khả năng của hành vi không xác định", đó là một lựa chọn hợp lý. Điều đó có nghĩa là bạn phải có một hành vi dự kiến ​​để chuyển nullđến Foo::Frobnicate(Widget widget).

Và bạn ghi lại quyết định đó bằng cách bao gồm một

[Test]
void Foo_FrobnicatesANullWidget_ThrowsInvalidArgument() 
{
    Given(Foo foo);
    When(foo.Frobnicate(null));
    Then(Expect_Exception(InvalidArgument));
}

1

Một bộ kiểm tra tốt sẽ thực hiện giao diện bên ngoài của lớp của bạn và đảm bảo rằng các hành vi lạm dụng đó tạo ra phản hồi chính xác (một ngoại lệ hoặc bất cứ điều gì bạn xác định là "chính xác"). Trong thực tế, trường hợp thử nghiệm đầu tiên mà tôi viết cho một lớp là gọi hàm tạo của nó với các đối số ngoài phạm vi.

Loại lập trình phòng thủ có xu hướng bị loại bỏ bởi cách tiếp cận được kiểm tra đơn vị đầy đủ là xác nhận không cần thiết các bất biến nội bộ không thể bị vi phạm bởi mã bên ngoài.

Một ý tưởng hữu ích đôi khi tôi sử dụng là cung cấp một phương pháp kiểm tra các bất biến của đối tượng; phương pháp phá bỏ của bạn có thể gọi nó để xác nhận rằng các hành động bên ngoài của bạn trên đối tượng không bao giờ phá vỡ các bất biến.


0

Các thử nghiệm của TDD sẽ bắt lỗi trong quá trình phát triển mã .

Các giới hạn kiểm tra mà bạn mô tả là một phần của lập trình phòng thủ sẽ bắt lỗi trong quá trình sử dụng mã .

Nếu hai tên miền giống nhau, đó là mã bạn đang viết chỉ được sử dụng nội bộ bởi dự án cụ thể này, thì có thể đúng là TDD sẽ loại trừ sự cần thiết của giới hạn lập trình phòng thủ mà bạn mô tả, nhưng chỉ khi các loại đó kiểm tra giới hạn được thực hiện cụ thể trong các bài kiểm tra TDD .


Như một ví dụ cụ thể, giả sử rằng một thư viện mã tài chính được phát triển bằng TDD. Một trong những thử nghiệm có thể khẳng định rằng một giá trị cụ thể không bao giờ có thể âm. Điều đó đảm bảo rằng các nhà phát triển của thư viện không vô tình sử dụng sai các lớp khi họ triển khai các tính năng.

Nhưng sau khi thư viện được phát hành và tôi đang sử dụng nó trong chương trình của riêng mình, các bài kiểm tra TDD đó không ngăn tôi gán giá trị âm (giả sử nó bị lộ). Giới hạn kiểm tra sẽ.

Quan điểm của tôi là trong khi một TDD khẳng định có thể giải quyết vấn đề giá trị âm nếu mã chỉ được sử dụng nội bộ như là một phần của sự phát triển của một ứng dụng lớn hơn (theo TDD), nếu đó sẽ là một thư viện được sử dụng bởi các lập trình viên khác mà không có TDD khuôn khổ và kiểm tra , giới hạn kiểm tra vấn đề.


1
Tôi đã không downvote, nhưng tôi đồng ý với các downvote với tiền đề là thêm sự khác biệt tinh tế cho loại tranh luận này làm vẩn đục nước.
Craig

@Craig Tôi quan tâm đến phản hồi của bạn về ví dụ cụ thể mà tôi đã thêm.
Blackhawk

Tôi thích tính cụ thể của ví dụ. Mối quan tâm duy nhất tôi vẫn có là chung cho toàn bộ cuộc tranh luận. Ví dụ; cùng với một số nhà phát triển mới trong nhóm và viết một thành phần mới sử dụng mô-đun tài chính đó. Anh chàng mới không nhận thức được tất cả những điều phức tạp của hệ thống, chứ đừng nói đến thực tế là tất cả các loại kiến ​​thức chuyên môn về cách hệ thống được cho là vận hành đều được nhúng trong các bài kiểm tra thay vì mã được kiểm tra.
Craig

Vì vậy, anh chàng / cô gái mới lỡ tạo ra một số thử nghiệm quan trọng, VÀ bạn kết thúc với sự dư thừa trong các thử nghiệm của mình - các thử nghiệm ở các phần khác nhau của hệ thống đang kiểm tra các điều kiện giống nhau và phát triển không nhất quán khi thời gian trôi qua, thay vì chỉ đưa ra các xác nhận phù hợp và kiểm tra điều kiện tiên quyết trong mã nơi hành động.
Craig

1
Một cái gì đó như thế. Ngoại trừ việc rất nhiều đối số ở đây là về việc kiểm tra mã cuộc gọi thực hiện tất cả các kiểm tra. Nhưng nếu bạn có bất kỳ mức độ nào của người hâm mộ, cuối cùng bạn sẽ thực hiện kiểm tra tương tự từ một loạt các địa điểm khác nhau và đó là vấn đề bảo trì trong chính nó. Điều gì sẽ xảy ra nếu phạm vi đầu vào hợp lệ cho một thủ tục thay đổi, nhưng bạn có kiến ​​thức về miền cho phạm vi đó được tích hợp trong các thử nghiệm thực hiện các thành phần khác nhau? Tôi vẫn hoàn toàn ủng hộ lập trình phòng thủ và sử dụng hồ sơ để xác định xem và khi nào bạn có vấn đề về hiệu suất để giải quyết.
Craig

0

TDD và lập trình phòng thủ đi đôi với tay. Sử dụng cả hai không phải là dư thừa, nhưng trên thực tế là bổ sung. Khi bạn có một chức năng, bạn muốn chắc chắn rằng chức năng đó hoạt động như được mô tả và viết các bài kiểm tra cho nó; nếu bạn không bao gồm những gì xảy ra khi trong trường hợp đầu vào xấu, trả lại kém, trạng thái xấu, v.v. thì bạn không viết bài kiểm tra của mình đủ mạnh và mã của bạn sẽ dễ bị hỏng ngay cả khi tất cả các bài kiểm tra của bạn đều vượt qua.

Là một kỹ sư nhúng, tôi muốn sử dụng ví dụ viết một hàm để chỉ cần thêm hai byte lại với nhau và trả về kết quả như thế này:

uint8_t AddTwoBytes(uint8_t a, uint8_t b, uint8_t *sum); 

Bây giờ nếu bạn chỉ đơn giản là *(sum) = a + bnó sẽ làm việc, nhưng chỉ với một số đầu vào. a = 1b = 2sẽ thực hiện sum = 3; tuy nhiên vì kích thước của tổng là một byte a = 100b = 200sẽ tạo ra sum = 44do tràn. Trong C, bạn sẽ trả về lỗi trong trường hợp này để biểu thị chức năng không thành công; ném một ngoại lệ là điều tương tự trong mã của bạn. Không xem xét sự thất bại hoặc thử nghiệm cách xử lý chúng sẽ không hoạt động lâu dài, bởi vì nếu những điều kiện đó xảy ra, chúng sẽ không được xử lý và có thể gây ra bất kỳ vấn đề nào.


Trông giống như một ví dụ câu hỏi phỏng vấn tốt (tại sao nó có giá trị trả về và tham số "out" - và điều gì xảy ra khi sumcon trỏ null?).
Toby Speight
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.