Các loại kiểm tra đơn vị dựa trên tính hữu ích


13

Từ quan điểm giá trị tôi thấy hai nhóm thử nghiệm đơn vị trong thực tế của tôi:

  1. Các thử nghiệm kiểm tra một số logic không tầm thường. Viết chúng (trước khi thực hiện hoặc sau) cho thấy một số vấn đề / lỗi tiềm ẩn và giúp tự tin trong trường hợp logic được thay đổi trong tương lai.
  2. Các thử nghiệm kiểm tra một số logic rất tầm thường. Những thử nghiệm này giống như mã tài liệu (thường là với giả) hơn là kiểm tra nó. Quy trình bảo trì của các thử nghiệm đó không phải là "một số logic đã thay đổi, thử nghiệm trở thành màu đỏ - cảm ơn Chúa tôi đã viết thử nghiệm này" nhưng "một số mã tầm thường đã thay đổi, thử nghiệm trở thành âm tính giả - tôi phải duy trì (viết lại) thử nghiệm mà không thu được lợi nhuận nào" . Hầu hết thời gian những bài kiểm tra đó không có giá trị duy trì (ngoại trừ lý do tôn giáo). Và theo kinh nghiệm của tôi trong nhiều hệ thống, các bài kiểm tra đó giống như 80% của tất cả các bài kiểm tra.

Tôi đang cố gắng tìm hiểu xem những người khác nghĩ gì về chủ đề kiểm tra đơn vị phân tách theo giá trị và cách nó tương ứng với sự phân tách của tôi. Nhưng những gì tôi chủ yếu nhìn thấy là tuyên truyền TDD toàn thời gian hoặc tuyên truyền thử nghiệm là vô dụng-chỉ-viết-mã-mã. Tôi quan tâm đến một cái gì đó ở giữa. Suy nghĩ của riêng bạn hoặc tài liệu tham khảo cho bài viết / giấy tờ / sách được chào đón.


3
Tôi giữ các bài kiểm tra đơn vị kiểm tra các lỗi đã biết (cụ thể) - đã từng trượt qua các bài kiểm tra đơn vị ban đầu - như một nhóm riêng biệt có vai trò là ngăn chặn các lỗi hồi quy.
Konrad Morawski

6
Những loại thử nghiệm thứ hai đó là những gì tôi xem là một loại "ma sát thay đổi". Đừng giảm giá hữu ích của họ. Thay đổi ngay cả sự tầm thường của mã có xu hướng tạo ra hiệu ứng gợn trong toàn bộ cơ sở mã hóa và giới thiệu loại ma sát này như một trở ngại cho các nhà phát triển của bạn để họ chỉ thay đổi những thứ thực sự cần nó, thay vì dựa trên một số sở thích cá nhân hay thay đổi.
Telastyn

3
@Telastyn - Tất cả mọi thứ về nhận xét của bạn dường như hoàn toàn điên rồ với tôi. Ai sẽ cố tình làm khó thay đổi mã? Tại sao không khuyến khích các nhà phát triển thay đổi mã khi họ thấy phù hợp - bạn không tin tưởng họ? Họ là những nhà phát triển tồi?
Benjamin Hodgson

2
Trong mọi trường hợp, nếu việc thay đổi mã có xu hướng có "hiệu ứng gợn" thì mã của bạn có vấn đề về thiết kế - trong trường hợp đó, các nhà phát triển nên được khuyến khích tái cấu trúc càng nhiều càng hợp lý. Các bài kiểm tra mong manh chủ động không khuyến khích tái cấu trúc (một bài kiểm tra thất bại; ai có thể bận tâm tìm hiểu xem bài kiểm tra đó có phải là một trong 80% bài kiểm tra không thực sự làm gì không? Bạn chỉ cần tìm một cách khác, phức tạp hơn để làm điều đó). Nhưng bạn dường như xem đây là một đặc điểm mong muốn ... Tôi không hiểu gì cả.
Benjamin Hodgson

2
Dù sao, OP có thể thấy bài đăng trên blog này từ người tạo ra Rails là thú vị. Để đơn giản hóa quá mức quan điểm của mình, có lẽ bạn nên cố gắng vứt bỏ 80% bài kiểm tra đó.
Benjamin Hodgson

Câu trả lời:


14

Tôi nghĩ rằng đó là tự nhiên để gặp một sự phân chia trong thử nghiệm đơn vị. Có nhiều ý kiến ​​khác nhau về cách thực hiện đúng và tự nhiên tất cả các ý kiến ​​khác vốn đã sai . Gần đây có khá nhiều bài viết về DrDobbs khám phá chính vấn đề này mà tôi liên kết ở cuối câu trả lời của mình.

Vấn đề đầu tiên tôi thấy với các bài kiểm tra là rất dễ khiến chúng sai. Trong lớp C ++ đại học của tôi, chúng tôi đã được tiếp xúc với các bài kiểm tra đơn vị trong cả học kỳ thứ nhất và thứ hai. Chúng tôi không biết gì về lập trình nói chung trong cả hai học kỳ - chúng tôi đã cố gắng học các nguyên tắc cơ bản của lập trình thông qua C ++. Bây giờ hãy tưởng tượng nói với các sinh viên, "Ồ này, bạn đã viết một máy tính thuế hàng năm! Bây giờ hãy viết một số bài kiểm tra đơn vị để đảm bảo rằng nó hoạt động chính xác." Kết quả rất rõ ràng - tất cả đều kinh khủng, bao gồm cả những nỗ lực của tôi.

Một khi bạn thừa nhận rằng bạn rất giỏi trong việc viết bài kiểm tra đơn vị và mong muốn tốt hơn, bạn sẽ sớm phải đối mặt với các phong cách thử nghiệm hợp thời trang hoặc các phương pháp khác nhau. Bằng các phương pháp thử nghiệm, tôi đề cập đến các thực tiễn như thử nghiệm đầu tiên hoặc những gì Andrew Binstock của DrDobbs thực hiện, đó là viết các thử nghiệm cùng với mã. Cả hai đều có ưu và nhược điểm và tôi từ chối đi vào bất kỳ chi tiết chủ quan nào vì điều đó sẽ kích động một cuộc chiến rực lửa. Nếu bạn không bối rối về phương pháp lập trình nào tốt hơn, thì có lẽ phong cách thử nghiệm sẽ tạo nên mánh khóe. Bạn có nên sử dụng TDD, BDD, thử nghiệm dựa trên tài sản? JUnit có các khái niệm nâng cao được gọi là Lý thuyết làm mờ ranh giới giữa TDD và thử nghiệm dựa trên Thuộc tính. Dùng khi nào?

tl; dr Rất dễ để kiểm tra sai, nó cực kỳ gây tranh cãi và tôi không tin rằng bất kỳ một phương pháp kiểm tra nào vốn đã tốt hơn miễn là chúng được sử dụng một cách chuyên nghiệp và chuyên nghiệp trong bối cảnh phù hợp. trong tâm trí tôi là một phần mở rộng cho các xác nhận hoặc kiểm tra độ tỉnh táo được sử dụng để đảm bảo một cách tiếp cận quảng cáo không thành công nhanh chóng mà giờ đây dễ dàng hơn nhiều.

Đối với một ý kiến ​​chủ quan, tôi thích viết "các giai đoạn" của các bài kiểm tra, vì thiếu một cụm từ tốt hơn. Tôi viết các bài kiểm tra đơn vị kiểm tra các lớp trong sự cô lập, sử dụng các mô phỏng khi cần thiết. Chúng có thể sẽ được thực thi với JUnit hoặc một cái gì đó tương tự. Sau đó tôi viết các bài kiểm tra tích hợp hoặc chấp nhận, chúng được chạy riêng và thường chỉ vài lần một ngày. Đây là những trường hợp sử dụng không tầm thường của bạn. Tôi thường sử dụng BDD vì thật tuyệt khi thể hiện các tính năng bằng ngôn ngữ tự nhiên, điều mà JUnit không thể dễ dàng cung cấp.

Cuối cùng, tài nguyên. Những điều này sẽ đưa ra các ý kiến ​​mâu thuẫn chủ yếu tập trung vào kiểm tra đơn vị bằng các ngôn ngữ khác nhau và với các khung khác nhau. Họ nên trình bày sự phân chia về ý thức hệ và phương pháp luận trong khi cho phép bạn đưa ra ý kiến ​​của riêng mình miễn là tôi chưa thao túng bạn quá nhiều :)

[1] Tham nhũng nhanh nhẹn của Andrew Binstock

[2] Phản hồi các phản hồi của bài viết trước

[3] Ứng phó với tham nhũng Agile của chú Bob

[4] Ứng phó với tham nhũng của Agile bởi Rob Myers

[5] Tại sao làm phiền với thử nghiệm dưa chuột?

[6] Bạn đang nói sai

[7] Bước ra khỏi công cụ

[8] Bình luận về 'Chữ số La Mã Kata với Bình luận'

[9] Chữ số La Mã Kata với bình luận


1
Một trong những tranh cãi thân thiện của tôi là nếu bạn đang viết bài kiểm tra để kiểm tra chức năng của máy tính thuế hàng năm, thì bạn không viết bài kiểm tra đơn vị. Đó là một bài kiểm tra tích hợp. Máy tính của bạn phải được chia thành các đơn vị thực hiện khá đơn giản và đơn vị của bạn sẽ kiểm tra các đơn vị đó. Nếu một trong những đơn vị đó ngừng hoạt động đúng cách (thử nghiệm bắt đầu không thành công), thì nó giống như loại bỏ một phần của tường móng và bạn cần sửa chữa mã (nói chung không phải là thử nghiệm). Hoặc là, hoặc bạn đã xác định một chút mã không còn cần thiết và cần được loại bỏ.
Craig

1
@Craig: Chính xác! Đây là những gì tôi có nghĩa là không biết làm thế nào để viết bài kiểm tra thích hợp. Là một sinh viên đại học, người thu thuế là một lớp lớn được viết mà không có sự hiểu biết đúng đắn về RẮN. Bạn hoàn toàn đúng khi nghĩ rằng đây là một bài kiểm tra tích hợp hơn bất kỳ điều gì khác, nhưng đó là một thuật ngữ không xác định đối với chúng tôi. Chúng tôi chỉ được tiếp xúc với các bài kiểm tra "đơn vị" của giáo sư của chúng tôi.
IAE

5

Tôi tin rằng điều quan trọng là phải có các thử nghiệm của cả hai loại và sử dụng chúng khi thích hợp.

Giống như bạn đã nói, có hai thái cực và tôi thực sự không đồng ý với một trong hai.

Điều quan trọng là các bài kiểm tra đơn vị phải bao gồm các quy tắc và yêu cầu kinh doanh . Nếu có yêu cầu hệ thống phải theo dõi tuổi của một người, hãy viết các bài kiểm tra "tầm thường" để đảm bảo tuổi là số nguyên không âm. Bạn đang kiểm tra miền dữ liệu theo yêu cầu của hệ thống: mặc dù tầm thường, nó có giá trị vì nó đang thực thi các tham số của hệ thống .

Tương tự như vậy với các thử nghiệm phức tạp hơn, chúng phải mang lại giá trị. Chắc chắn, bạn có thể viết một bài kiểm tra xác nhận một cái gì đó không phải là một yêu cầu nhưng nên được thi hành trong một tháp ngà ở đâu đó, nhưng đó là thời gian tốt hơn để viết các bài kiểm tra xác nhận các yêu cầu mà khách hàng trả cho bạn. Ví dụ, tại sao viết một bài kiểm tra xác thực mã của bạn có thể xử lý một luồng đầu vào hết thời gian, khi các luồng duy nhất là từ các tệp cục bộ chứ không phải mạng?

Tôi tin tưởng chắc chắn vào thử nghiệm đơn vị và sử dụng TDD bất cứ nơi nào nó có ý nghĩa. Các thử nghiệm đơn vị chắc chắn mang lại giá trị dưới dạng tăng chất lượng và hành vi "không nhanh" khi thay đổi mã. Tuy nhiên, có một quy tắc 80/20 cũ cần ghi nhớ. Tại một số điểm, bạn sẽ đạt được lợi nhuận giảm dần khi viết bài kiểm tra và bạn cần chuyển sang công việc hiệu quả hơn ngay cả khi có một số giá trị có thể đo được từ việc viết thêm bài kiểm tra.


Viết bài kiểm tra để đảm bảo rằng hệ thống theo dõi tuổi của một người không phải là bài kiểm tra đơn vị, IMO. Đó là một bài kiểm tra tích hợp. Một bài kiểm tra đơn vị sẽ kiểm tra đơn vị thực hiện chung (còn gọi là "thủ tục"), giả sử, tính một giá trị tuổi từ, giả sử, ngày cơ sở và phần bù trong bất kỳ đơn vị nào (ngày, tuần, v.v.). Quan điểm của tôi là một chút mã không nên có bất kỳ sự phụ thuộc kỳ lạ nào vào phần còn lại của hệ thống. Nó CHỈ tính tuổi từ một vài giá trị đầu vào và trong trường hợp đó, kiểm tra đơn vị có thể xác nhận hành vi chính xác, có thể đưa ra một ngoại lệ nếu phần bù tạo ra tuổi âm.
Craig

Tôi đã không đề cập đến bất kỳ tính toán. Nếu một mô hình lưu trữ một phần dữ liệu, nó có thể xác nhận dữ liệu thuộc về miền chính xác. Trong trường hợp này, miền là tập hợp các số nguyên không âm. Tính toán nên xảy ra trong bộ điều khiển (trong MVC) và trong ví dụ này, phép tính tuổi sẽ là một thử nghiệm riêng.

4

Đây là sự đảm nhận của tôi: tất cả các bài kiểm tra đều có chi phí:

  • thời gian và nỗ lực ban đầu:
    • suy nghĩ về những gì cần kiểm tra và làm thế nào để kiểm tra nó
    • thực hiện thử nghiệm và đảm bảo rằng nó đang thử nghiệm những gì nó được cho là
  • đang duy trì
    • đảm bảo kiểm tra vẫn đang thực hiện những gì nó phải làm khi mã phát triển tự nhiên
  • chạy thử
    • thời gian thực hiện
    • phân tích kết quả

Chúng tôi cũng có ý định cho tất cả các thử nghiệm để cung cấp lợi ích (và theo kinh nghiệm của tôi, gần như tất cả các thử nghiệm đều cung cấp lợi ích):

  • sự chỉ rõ
  • trường hợp góc nổi bật
  • ngăn chặn hồi quy
  • xác minh tự động
  • ví dụ về sử dụng API
  • định lượng các tính chất cụ thể (thời gian, không gian)

Vì vậy, thật dễ dàng để thấy rằng nếu bạn viết một loạt các bài kiểm tra, có lẽ chúng sẽ có một số giá trị. Trường hợp điều này trở nên phức tạp là khi bạn bắt đầu so sánh giá trị đó (nhân tiện, bạn có thể không biết trước - nếu bạn vứt bỏ mã của mình, các bài kiểm tra hồi quy sẽ mất giá trị của chúng) với chi phí.

Bây giờ, thời gian và nỗ lực của bạn bị hạn chế. Bạn muốn chọn làm những việc mang lại lợi ích cao nhất với chi phí thấp nhất. Và tôi nghĩ rằng đó là một điều rất khó để làm, không chỉ bởi vì nó có thể đòi hỏi kiến ​​thức mà một người không có hoặc sẽ tốn kém để có được.

Và đó là sự cọ xát thực sự giữa các phương pháp khác nhau. Tôi tin rằng tất cả họ đã xác định các chiến lược thử nghiệm có lợi. Tuy nhiên, mỗi chiến lược có chi phí và lợi ích khác nhau nói chung. Ngoài ra, chi phí và lợi ích của mỗi chiến lược có thể sẽ phụ thuộc rất nhiều vào các chi tiết cụ thể của dự án, tên miền và nhóm. Nói cách khác, có thể có nhiều câu trả lời tốt nhất.

Trong một số trường hợp, việc bơm ra mã mà không cần kiểm tra có thể mang lại lợi ích / chi phí tốt nhất. Trong các trường hợp khác, một bộ kiểm tra kỹ lưỡng có thể tốt hơn. Trong các trường hợp khác, cải thiện thiết kế có thể là điều tốt nhất để làm.


2

Có gì một đơn vị kiểm tra, thực sự? Và có thực sự có một sự phân đôi lớn như vậy trong chơi ở đây?

Chúng tôi làm việc trong một lĩnh vực mà việc đọc theo nghĩa đen một chút qua phần cuối của bộ đệm hoàn toàn có thể làm hỏng chương trình hoặc khiến nó tạo ra kết quả hoàn toàn không chính xác hoặc được chứng minh bằng lỗi TLS "HeartBleed" gần đây, đặt một hệ thống được cho là an toàn mở mà không tạo ra bất kỳ bằng chứng trực tiếp của lỗ hổng.

Không thể loại bỏ tất cả sự phức tạp khỏi các hệ thống này. Nhưng công việc của chúng tôi là, ở mức độ có thể, để giảm thiểu và quản lý sự phức tạp đó.

Là một thử nghiệm đơn vị một thử nghiệm xác nhận, ví dụ, đặt chỗ được đăng thành công trong ba hệ thống khác nhau, một mục nhật ký được tạo và xác nhận Email được gửi đi?

Tôi sẽ nói không . Đó là một sự tích hợp bài kiểm tra . Và những người chắc chắn có vị trí của họ, nhưng họ cũng là một chủ đề khác.

Một thử nghiệm tích hợp hoạt động để xác nhận chức năng tổng thể của toàn bộ "tính năng". Nhưng mã đằng sau tính năng đó nên được chia thành các khối xây dựng đơn giản, có thể kiểm tra, còn gọi là "đơn vị".

Vì vậy, một bài kiểm tra đơn vị nên có phạm vi rất hạn chế.

Điều đó ngụ ý rằng mã được kiểm tra bởi bài kiểm tra đơn vị nên có phạm vi rất hạn chế.

Điều đó ngụ ý thêm rằng một trong những trụ cột của thiết kế tốt là chia vấn đề phức tạp của bạn thành các phần nhỏ hơn, đơn mục đích (trong phạm vi có thể) có thể được kiểm tra cách ly tương đối với nhau.

Những gì bạn kết thúc là một hệ thống được tạo thành từ các thành phần nền tảng đáng tin cậy và bạn biết liệu có bất kỳ đơn vị cơ bản nào của mã bị phá vỡ không vì bạn đã viết các bài kiểm tra phạm vi đơn giản, nhỏ, giới hạn để cho bạn biết chính xác điều đó.

Trong nhiều trường hợp, bạn cũng có thể có nhiều bài kiểm tra cho mỗi đơn vị. Các bài kiểm tra nên đơn giản, kiểm tra một và chỉ một hành vi trong phạm vi có thể.

Khái niệm "kiểm tra đơn vị" kiểm tra logic không tầm thường, phức tạp, phức tạp là, tôi nghĩ, một chút của một oxymoron.

Vì vậy, nếu sự cố thiết kế kiểu cố ý đó xảy ra, thì làm thế nào một thử nghiệm đơn vị trên thế giới có thể đột nhiên bắt đầu tạo ra dương tính giả, trừ khi chức năng cơ bản của đơn vị mã được kiểm tra đã thay đổi? Và nếu điều đó đã xảy ra, thì bạn nên tin rằng có một số hiệu ứng gợn không rõ ràng trong trò chơi. Thử nghiệm bị hỏng của bạn, một thử nghiệm dường như tạo ra dương tính giả, thực sự cảnh báo bạn rằng một số thay đổi đã phá vỡ một vòng tròn phụ thuộc rộng hơn trong cơ sở mã, và nó cần được kiểm tra và sửa chữa.

Một số trong những đơn vị đó (nhiều trong số chúng) có thể cần phải được kiểm tra bằng cách sử dụng các đối tượng giả, nhưng điều đó không có nghĩa là bạn phải viết các bài kiểm tra phức tạp hoặc phức tạp hơn.

Trở lại với ví dụ contrived của tôi về một hệ thống đặt phòng, bạn thực sự có thể không được gửi yêu cầu giảm đến một cơ sở dữ liệu đặt phòng trực tiếp hoặc dịch vụ của bên thứ ba (hoặc thậm chí là một "dev" thể hiện của nó) mỗi khi bạn đơn vị kiểm tra mã của bạn.

Vì vậy, bạn sử dụng giả mà trình bày hợp đồng giao diện tương tự. Các thử nghiệm sau đó có thể xác nhận hành vi của một đoạn mã xác định tương đối nhỏ. Màu xanh lá cây xuống bảng sau đó cho bạn biết rằng các khối bao gồm nền tảng của bạn không bị phá vỡ.

Nhưng logic của các đơn vị kiểm tra cá nhân vẫn đơn giản nhất có thể.


1

Tất nhiên, đây chỉ là ý kiến ​​của tôi, nhưng đã dành vài tháng qua để học lập trình chức năng trong fsharp (đến từ nền tảng C #) đã khiến tôi nhận ra một vài điều.

Như OP đã nêu, thường có 2 loại "bài kiểm tra đơn vị" mà chúng ta thấy hàng ngày. Các thử nghiệm bao gồm các phương pháp trong và ngoài phương pháp, thường có giá trị nhất, nhưng khó thực hiện đối với 80% hệ thống ít về "thuật toán" và nhiều hơn về "trừu tượng hóa".

Một loại khác, là thử nghiệm tính tương tác trừu tượng, thường liên quan đến chế độ chế nhạo. Theo ý kiến ​​của tôi, thử nghiệm này chủ yếu là cần thiết do thiết kế ứng dụng của bạn. Ommit chúng, và bạn có nguy cơ lỗi lạ và mã spagetti, bởi vì mọi người không nghĩ về thiết kế của họ đúng cách trừ khi họ bị buộc phải làm các thử nghiệm đầu tiên (và thậm chí sau đó, thường làm hỏng nó). Vấn đề không phải là quá nhiều phương pháp thử nghiệm, mà là thiết kế cơ bản của hệ thống. Hầu hết các hệ thống được xây dựng với các ngôn ngữ bắt buộc hoặc OO đều có sự phụ thuộc hoàn toàn vào "tác dụng phụ" hay còn gọi là "Làm điều này, nhưng đừng nói với tôi bất cứ điều gì". Khi bạn dựa vào tác dụng phụ, bạn cần kiểm tra nó, bởi vì một yêu cầu kinh doanh hoặc hoạt động thường là một phần của nó.

Khi bạn thiết kế hệ thống của mình theo cách có nhiều chức năng hơn, trong đó bạn tránh xây dựng sự phụ thuộc vào tác dụng phụ và tránh thay đổi trạng thái / theo dõi thông qua tính không thay đổi, nó cho phép bạn tập trung nhiều hơn vào các thử nghiệm "trong và ngoài", kiểm tra rõ ràng hơn hành động và ít hơn cách bạn đến đó. Bạn sẽ ngạc nhiên về những thứ như bất biến có thể mang lại cho bạn những giải pháp đơn giản hơn nhiều cho cùng một vấn đề và khi bạn không còn phụ thuộc vào "tác dụng phụ", bạn có thể làm những việc như lập trình song song và lập trình không đồng bộ mà hầu như không phải trả thêm phí.

Kể từ khi tôi bắt đầu viết mã trong Fsharp, tôi không cần một khung mô phỏng cho bất cứ điều gì, và thậm chí đã loại bỏ hoàn toàn sự phụ thuộc của tôi vào Container IOC. Các thử nghiệm của tôi được thúc đẩy bởi nhu cầu và giá trị kinh doanh, và không phải trên các lớp trừu tượng nặng thường cần thiết để đạt được thành phần trong lập trình mệnh lệnh.

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.