Làm thế nào tôi có thể ủng hộ thử nghiệm đơn vị trên mã riêng?


15

Tôi đang cố gắng ủng hộ thử nghiệm đơn vị trong nhóm làm việc của mình, nhưng một sự phản đối mà tôi thường gặp là nó chỉ được sử dụng cho API xuất bên ngoài (chỉ là một phần tối thiểu và không quan trọng trong hệ thống của chúng tôi), chứ không phải trên nội bộ và riêng tư mã (mà bây giờ chỉ có thử nghiệm chức năng).

Trong khi tôi nghĩ rằng bài kiểm tra đơn vị có thể và nên được áp dụng cho tất cả các mã, làm thế nào tôi có thể thuyết phục đồng nghiệp của mình?


3
Nếu bạn có các phương thức riêng tư mà bạn cảm thấy cần phải kiểm tra, thì đó thường là dấu hiệu cho thấy mã của bạn đang vi phạm SRP và có một lớp khác trong đó kêu lên để được trích xuất và kiểm tra theo cách riêng của nó.
Paddyslacker

@Paddyslacker: Tôi cảm thấy rằng tất cả các mã cần phải được kiểm tra. Tôi không thấy lý do tại sao một đơn vị mã tuân theo nguyên tắc trách nhiệm duy nhất không nên chịu thử nghiệm đơn vị ...
Wizard79

4
@lorenzo, bạn đã bỏ lỡ quan điểm của tôi; có lẽ tôi đã không làm cho nó rất tốt. Nếu bạn trích xuất các phương thức riêng tư này sang một lớp khác, bây giờ chúng sẽ cần có thể truy cập được từ lớp ban đầu của bạn. Bởi vì các phương thức hiện đang công khai, chúng sẽ cần phải được kiểm tra. Tôi không ngụ ý rằng chúng không nên được thử nghiệm, tôi đã ngụ ý rằng nếu bạn cảm thấy cần phải trực tiếp thử nghiệm các phương pháp, có khả năng chúng không nên riêng tư.
Paddyslacker

@Paddyslacker: Tôi cảm thấy cần phải trực tiếp kiểm tra các phương thức riêng tư. Tại sao bạn nghĩ rằng họ không nên riêng tư?
Wizard79

6
Bằng cách thử nghiệm các phương thức riêng tư, bạn sẽ phá vỡ sự trừu tượng. Bạn nên kiểm tra trạng thái và / hoặc hành vi, không thực hiện, trong các bài kiểm tra đơn vị. Các ví dụ / kịch bản của bạn sẽ có thể xác minh kết quả của mã riêng là gì - nếu bạn thấy khó khăn, thì như Paddyslacker nói rằng điều đó có thể có nghĩa là bạn đang vi phạm SRP. Điều đó cũng có nghĩa là mặc dù bạn chưa chắt lọc các ví dụ của mình để thực sự đại diện cho những gì mã của bạn đang làm.
FinnNk

Câu trả lời:


9

Đồng nghiệp của bạn có thể nhầm lẫn các bài kiểm tra đơn vị thực sự với các bài kiểm tra tích hợp. Nếu sản phẩm của bạn là (hoặc có) API, các thử nghiệm tích hợp có thể được lập trình dưới dạng các trường hợp thử nghiệm NUnit. Một số người lầm tưởng rằng đó là những bài kiểm tra đơn vị.

Bạn có thể cố gắng thuyết phục đồng nghiệp của mình bằng những điều sau (Tôi chắc chắn bạn đã biết công cụ này, tất cả những gì tôi nói là chỉ ra cho đồng nghiệp của bạn có thể giúp đỡ):

  • Kiểm tra phạm vi bảo hiểm . Đo tỷ lệ bao phủ thử nghiệm thực tế của các thử nghiệm tích hợp. Đây là một kiểm tra thực tế cho những người chưa bao giờ chạy thử nghiệm bảo hiểm. Bởi vì rất khó để thực hiện tất cả các đường dẫn logic khi đầu vào cách một vài lớp, phạm vi kiểm tra đứng đầu ở đâu đó trong khoảng từ 20% đến 50%. Để có được phạm vi bảo hiểm nhiều hơn, đồng nghiệp của bạn cần phải viết các bài kiểm tra đơn vị thực sự, bị cô lập.
  • Cấu hình . Triển khai cùng một phần mềm đang được thử nghiệm và có lẽ bạn có thể chứng minh cho đồng nghiệp thấy mức độ khó khăn khi chạy thử nghiệm của họ trong một môi trường khác. Đường dẫn đến các tệp khác nhau, chuỗi kết nối DB, URL của các dịch vụ từ xa, v.v. - tất cả đều cộng lại.
  • Thời gian thực hiện . Trừ khi các bài kiểm tra là bài kiểm tra đơn vị thực sự và có thể chạy trong bộ nhớ, chúng sẽ mất rất nhiều thời gian để chạy.

12

Các lý do để sử dụng kiểm tra đơn vị trên mã nội bộ / riêng tư giống hệt như các API được hỗ trợ bên ngoài:

  • Chúng ngăn chặn lỗi lặp lại (bài kiểm tra đơn vị là một phần của bộ kiểm tra hồi quy của bạn).
  • Họ tài liệu (trong một định dạng thực thi!) Rằng mã hoạt động.
  • Họ cung cấp một định nghĩa thực thi về "mã hoạt động" nghĩa là gì.
  • Họ cung cấp một phương tiện tự động để chứng minh rằng mã thực sự khớp với thông số kỹ thuật (như được xác định bởi điểm trên).
  • Chúng cho thấy cách đơn vị / lớp / mô-đun / chức năng / phương thức không thành công khi có đầu vào bất ngờ.
  • Họ cung cấp các ví dụ về cách sử dụng đơn vị, đây là tài liệu tuyệt vời cho các thành viên mới của nhóm.

8

Nếu bạn có nghĩa là riêng tư theo cách tôi nghĩ bạn có nghĩa là nó, thì không - bạn không nên đơn vị kiểm tra nó. Bạn chỉ nên là đơn vị kiểm tra hành vi / trạng thái quan sát được. Bạn có thể đang thiếu điểm đằng sau chu trình "đỏ-xanh-tái cấu trúc" của TDD (và nếu bạn không thực hiện kiểm tra trước thì áp dụng nguyên tắc tương tự). Khi các bài kiểm tra được viết và vượt qua, bạn không muốn chúng thay đổi trong khi thực hiện tái cấu trúc. Nếu bạn buộc phải kiểm tra đơn vị chức năng riêng tư thì có lẽ điều đó có nghĩa là đơn vị kiểm tra xung quanh chức năng công cộng bị thiếu sót. Nếu việc viết các bài kiểm tra xung quanh mã công khai là khó khăn và phức tạp thì có thể lớp của bạn đang làm quá nhiều hoặc vấn đề của bạn không được xác định rõ ràng.

Tồi tệ hơn, theo thời gian các bài kiểm tra đơn vị của bạn sẽ trở thành một quả bóng và chuỗi làm bạn chậm lại mà không thêm bất kỳ giá trị nào (thay đổi thực hiện, ví dụ như tối ưu hóa hoặc loại bỏ trùng lặp, sẽ không ảnh hưởng đến các bài kiểm tra đơn vị). Tuy nhiên, mã nội bộ phải được kiểm tra đơn vị vì hành vi / trạng thái có thể quan sát được (chỉ trong một cách hạn chế).

Khi tôi lần đầu tiên thực hiện kiểm tra đơn vị, tôi đã rút ra tất cả các loại thủ thuật để kiểm tra đơn vị riêng tư nhưng bây giờ, với một vài năm trong vành đai của tôi, tôi thấy nó còn tệ hơn cả lãng phí thời gian.

Đây là một ví dụ ngớ ngẩn, tất nhiên trong cuộc sống thực, bạn sẽ có nhiều bài kiểm tra hơn:

Giả sử bạn có một lớp trả về một danh sách các chuỗi được sắp xếp - bạn nên kiểm tra xem kết quả đã được sắp xếp chưa, chứ không phải cách nó thực sự sắp xếp danh sách đó. Bạn có thể bắt đầu thực hiện với một thuật toán duy nhất sắp xếp danh sách. Khi đã xong, bài kiểm tra của bạn không cần thay đổi nếu bạn thay đổi thuật toán sắp xếp của mình. Tại thời điểm này, bạn có một bài kiểm tra duy nhất (giả sử rằng việc sắp xếp được nhúng trong lớp của bạn):

  1. Là kết quả của tôi được sắp xếp?

Bây giờ hãy nói rằng bạn muốn có hai thuật toán (có thể một thuật toán hiệu quả hơn trong một số trường hợp nhưng không phải là thuật toán khác), thì mỗi thuật toán có thể (và nói chung, nên) được cung cấp bởi một lớp khác và lớp của bạn chọn từ chúng - bạn có thể kiểm tra điều này đang xảy ra kịch bản đã chọn của bạn bằng cách sử dụng giả, nhưng thử nghiệm ban đầu của bạn vẫn hợp lệ và vì chúng tôi chỉ xác minh hành vi / trạng thái có thể quan sát được nên không cần thay đổi. Bạn kết thúc với 3 bài kiểm tra:

  1. Là kết quả của tôi được sắp xếp?
  2. Đưa ra một kịch bản (giả sử danh sách ban đầu gần như được sắp xếp để bắt đầu) là một cuộc gọi được thực hiện cho lớp sắp xếp các chuỗi bằng thuật toán X?
  3. Đưa ra một kịch bản (danh sách ban đầu theo thứ tự ngẫu nhiên) là một cuộc gọi được thực hiện cho lớp sắp xếp các chuỗi bằng thuật toán Y?

Giải pháp thay thế sẽ là bắt đầu kiểm tra mã riêng trong lớp của bạn - bạn không thu được gì từ điều này - các thử nghiệm trên cho tôi biết mọi thứ mà tôi cần biết khi có liên quan đến kiểm thử đơn vị. Bằng cách thêm các bài kiểm tra riêng tư, bạn sẽ tự tạo cho mình một chiếc áo khoác thẳng, sẽ còn bao nhiêu công việc nữa nếu bạn không chỉ kiểm tra xem kết quả đã được sắp xếp mà còn cả cách sắp xếp?

Các thử nghiệm (thuộc loại này) chỉ nên thay đổi khi hành vi thay đổi, bắt đầu viết thử nghiệm đối với mã riêng và điều đó xuất hiện ngoài cửa sổ.


1
Có lẽ có một sự hiểu lầm về ý nghĩa của "riêng tư". Trong hệ thống của chúng tôi, 99% mã là "riêng tư", sau đó chúng tôi có một API nhỏ để tự động hóa / điều khiển từ xa một trong các thành phần của hệ thống. Tôi có nghĩa là đơn vị kiểm tra mã của tất cả các mô-đun khác.
Wizard79

4

Đây là một lý do khác: trong trường hợp giả định tôi sẽ phải chọn giữa đơn vị kiểm tra API bên ngoài so với các phần riêng tư, tôi sẽ chọn các phần riêng tư.

Nếu mọi phần riêng tư được bao phủ bởi một thử nghiệm, API bao gồm các phần riêng tư này cũng sẽ được bao phủ gần như 100%, ngoại trừ chỉ dành cho lớp trên. Nhưng đó có khả năng là một lớp mỏng.

Mặt khác, khi chỉ kiểm tra API, có thể rất khó để bao quát đầy đủ tất cả các đường dẫn mã có thể.


+1 "mặt khác ..." Nhưng nếu không có gì khác, hãy thêm các bài kiểm tra trong đó một thất bại sẽ làm tổn thương nhiều nhất.
Tony Enni

2

Thật khó để khiến mọi người chấp nhận thử nghiệm đơn vị vì có vẻ như lãng phí thời gian ("chúng tôi có thể mã hóa một dự án kiếm tiền khác!") Hoặc đệ quy ("Và sau đó chúng tôi phải viết trường hợp thử nghiệm cho các trường hợp thử nghiệm!") Tôi có tội khi nói cả hai.

Lần đầu tiên bạn tìm thấy một lỗi, bạn phải đối mặt với sự thật rằng bạn không hoàn hảo (các lập trình viên chúng tôi quên nhanh như thế nào!) Và bạn nói, "Hmmm."


Một khía cạnh khác của kiểm thử đơn vị là mã phải được viết để có thể kiểm tra được. Nhận ra rằng một số Mã có thể dễ dàng kiểm tra và Một số Mã không làm cho một lập trình viên giỏi đi "Hmmm."


Bạn có hỏi đồng nghiệp tại sao kiểm tra đơn vị chỉ hữu ích cho các API đối diện bên ngoài không?


Một cách để hiển thị giá trị của kiểm thử đơn vị là chờ đợi một lỗi khó chịu xảy ra và sau đó cho thấy cách kiểm tra đơn vị có thể ngăn chặn nó. Đó không phải là để chà xát vào mặt họ, trong suy nghĩ của họ, chuyển thử nghiệm đơn vị từ một lý thuyết Tháp Ngà sang một thực tế trong chiến hào.

Một cách khác là đợi cho đến khi cùng một lỗi xảy ra hai lần . "Uhhh, thưa ông chủ, chúng tôi đã thêm mã để kiểm tra null sau sự cố tuần trước, nhưng lần này người dùng đã nhập một thứ trống rỗng!"


Dẫn bằng ví dụ. Viết bài kiểm tra đơn vị cho mã CỦA BẠN, sau đó hiển thị giá trị của sếp. Sau đó xem ông chủ sẽ gọi pizza vào bữa trưa một ngày và thuyết trình.


Cuối cùng, tôi không thể nói với bạn sự nhẹ nhõm mà tôi cảm thấy khi chúng tôi chuẩn bị chuyển sang sản phẩm và tôi nhận được một thanh màu xanh lá cây từ các bài kiểm tra đơn vị.


2

Có hai loại mã tin: Mã tin đó được gọi bởi mã công cộng (hoặc mã tin đó được gọi bởi mã tin đó được gọi bởi mã công cộng (hoặc ...)) và mã tin rằng không phải cuối cùng được gọi bằng công mã.

Cái trước đã được kiểm tra thông qua các bài kiểm tra cho mã công khai. Cái sau không thể được gọi ở tất cả và do đó nên được xóa, không được kiểm tra.

Lưu ý rằng khi bạn thực hiện TDD, không thể tồn tại mã riêng chưa được kiểm tra.


Trong hệ thống của chúng tôi, 99% mã thuộc loại thứ ba : riêng tư, không được gọi bởi mã công khai và cần thiết cho hệ thống (chỉ một phần tối thiểu trong hệ thống của chúng tôi có API công khai, bên ngoài).
Wizard79

1
"Lưu ý rằng khi bạn thực hiện TDD, không thể tồn tại mã riêng chưa được kiểm tra." <- xóa một trường hợp thử nghiệm mà không biết rằng thử nghiệm đó là thử nghiệm duy nhất bao trùm một nhánh cụ thể. OK, đó là mã "hiện chưa được kiểm tra", nhưng đủ dễ dàng để thấy một phép tái cấu trúc tầm thường sau đó thay đổi mã đó ... chỉ có bộ thử nghiệm của bạn không còn bao gồm nó nữa.
Frank Shearar

2

Kiểm thử đơn vị là tất cả về các đơn vị kiểm tra mã của bạn. Tùy thuộc vào bạn để xác định một đơn vị là gì. Đồng nghiệp của bạn xác định các đơn vị là các yếu tố API.

Dù sao, API thử nghiệm cũng sẽ dẫn đến việc sử dụng mã riêng. Nếu bạn xác định phạm vi bảo hiểm mã là một chỉ báo về tiến trình kiểm tra đơn vị, bạn sẽ kết thúc việc kiểm tra tất cả mã của mình. Nếu một số phần của mã chưa đạt được, hãy cho đồng nghiệp của bạn ba lựa chọn:

  • định nghĩa một trường hợp thử nghiệm khác để bao gồm phần đó,
  • phân tích mã để chứng minh lý do tại sao nó không thể được đề cập trong bối cảnh thử nghiệm đơn vị nhưng nên được đề cập trong các tình huống khác,
  • loại bỏ mã chết mà không được bảo hiểm không được chứng minh.

Trong hệ thống của chúng tôi, API chỉ là một phần tối thiểu, cho phép tự động hóa / điều khiển từ xa cho ứng dụng của bên thứ ba. Chỉ kiểm tra các tài khoản API để có độ bao phủ mã 1% ...
Wizard79
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.