Tại sao đơn vị thử nghiệm phương pháp tư nhân được coi là thực hành xấu?


16

Bối cảnh:

Tôi hiện đang làm việc trên một dự án nhỏ trong Python. Tôi thường cấu trúc các lớp của mình với một số phương thức công khai được ghi lại nhưng chủ yếu xử lý các khái niệm mức cao (những gì người dùng của lớp nên biết và sử dụng) và một loạt các phương thức ẩn (bắt đầu bằng dấu gạch dưới) chịu trách nhiệm về xử lý mức độ phức tạp hoặc thấp.

Tôi biết rằng các thử nghiệm là điều cần thiết để cung cấp sự tự tin về mã và để đảm bảo rằng mọi sửa đổi sau này không phá vỡ hành vi trước đó.

Vấn đề:

Để xây dựng các phương thức công khai cấp cao hơn trên cơ sở đáng tin cậy, tôi thường thử nghiệm các phương thức riêng tư. Tôi thấy dễ dàng hơn để tìm hiểu xem một sửa đổi mã đã đưa ra hồi quy và ở đâu. Điều đó có nghĩa là những thử nghiệm nội bộ đó có thể vi phạm các sửa đổi nhỏ và sẽ cần phải được sửa chữa / thay thế

Nhưng tôi cũng biết rằng phương pháp thử nghiệm đơn vị ít nhất là một khái niệm gây tranh cãi hoặc thường được coi là thực tiễn xấu. Lý do là: chỉ nên kiểm tra hành vi công khai (tham khảo )

Câu hỏi:

Tôi quan tâm đến việc làm theo các thực tiễn tốt nhất và muốn hiểu:

  • Tại sao sử dụng thử nghiệm đơn vị trên các phương thức riêng tư / ẩn lại có hại (rủi ro là gì)?
  • các thực tiễn tốt nhất khi các phương thức công cộng có thể sử dụng mức độ thấp và / hoặc xử lý phức tạp là gì?

Phòng ban:

  • nó không phải là một cách câu hỏi. Python không có khái niệm thực sự về quyền riêng tư và các phương thức ẩn đơn giản là không được liệt kê nhưng có thể được sử dụng khi bạn biết tên của chúng
  • Tôi chưa bao giờ được dạy các quy tắc và mô hình lập trình: các lớp học cuối cùng của tôi là từ những năm 80 ... Tôi chủ yếu học các ngôn ngữ bằng thử nghiệm và thất bại và các tài liệu tham khảo trên Internet (Stack Exchange là sở thích của tôi trong nhiều năm)


2
OP, bạn đã nghe hoặc đọc ở đâu rằng việc thử nghiệm các phương thức riêng tư được coi là "xấu"? Có nhiều cách khác nhau để kiểm tra đơn vị. Xem Kiểm tra đơn vị, kiểm tra hộp đen và kiểm tra hộp trắng .
John Wu

@JohnWu: Tôi biết sự khác biệt giữa thử nghiệm Hộp trắng và Hộp đen. Nhưng ngay cả trong thử nghiệm White-box, có vẻ như việc thử nghiệm các phương thức riêng tư là một gợi ý cho một vấn đề thiết kế. Câu hỏi của tôi là một nỗ lực để hiểu những con đường tốt nhất khi tôi rơi ở đó ...
Serge Ballesta

2
Một lần nữa, bạn đã nghe hoặc đọc rằng ngay cả trong thử nghiệm Hộp trắng, việc kiểm tra các phương thức riêng tư là một gợi ý cho một vấn đề thiết kế? Tôi muốn hiểu lý do đằng sau niềm tin đó trước khi thử trả lời.
John Wu

@SergeBallesta nói cách khác, đặt một số tài liệu tham khảo cho các bài viết đó khiến bạn tin rằng thử nghiệm phương pháp riêng tư là một thực tiễn xấu. Sau đó giải thích cho chúng tôi tại sao bạn tin họ.
Laiv

Câu trả lời:


17

Một vài lý do:

  1. Thông thường, khi bạn muốn thử nghiệm phương thức riêng tư của một lớp, đó là mùi thiết kế (lớp băng, không đủ các thành phần công cộng có thể tái sử dụng, v.v.). Hầu như luôn luôn có một số vấn đề "lớn hơn" lúc chơi.

  2. Bạn có thể kiểm tra chúng thông qua giao diện công cộng (đó là cách bạn muốn kiểm tra chúng, bởi vì đó là cách khách hàng sẽ gọi / sử dụng chúng). Bạn có thể có được cảm giác an toàn sai lầm bằng cách nhìn thấy đèn xanh trên tất cả các bài kiểm tra vượt qua cho các phương pháp riêng tư của bạn. Sẽ tốt hơn / an toàn hơn khi kiểm tra các trường hợp cạnh trên các chức năng riêng tư của bạn thông qua giao diện chung.

  3. Bạn có nguy cơ trùng lặp kiểm tra nghiêm trọng (các xét nghiệm trông / cảm thấy rất giống nhau) bằng cách thử nghiệm các phương pháp riêng tư. Điều này có hậu quả lớn khi các yêu cầu thay đổi, vì nhiều thử nghiệm hơn mức cần thiết sẽ bị phá vỡ. Nó cũng có thể đặt bạn vào một vị trí khó tái cấu trúc vì bộ thử nghiệm của bạn ... đó là điều trớ trêu cuối cùng, bởi vì bộ thử nghiệm có mặt để giúp bạn thiết kế lại và tái cấu trúc một cách an toàn!

Một mẹo nếu bạn vẫn muốn kiểm tra các phần riêng tư (không sử dụng nó nếu nó làm phiền bạn và YMMV, nhưng trước đây nó hoạt động tốt với tôi): Đôi khi, viết bài kiểm tra đơn vị cho các chức năng riêng tư chỉ để đảm bảo chúng hoạt động chính xác như bạn nghĩ chúng có thể có giá trị (đặc biệt nếu bạn chưa quen với ngôn ngữ). Tuy nhiên, sau khi bạn chắc chắn rằng chúng hoạt động, hãy xóa các bài kiểm tra và luôn đảm bảo rằng các bài kiểm tra đối mặt công khai là vững chắc và sẽ nắm bắt nếu ai đó thực hiện một thay đổi lớn đối với chức năng riêng tư.

Khi nào nên thử nghiệm các phương pháp riêng tư: Vì câu trả lời này đã trở nên phổ biến (phần nào), tôi cảm thấy bắt buộc phải đề cập đến việc "thực hành tốt nhất" luôn luôn là: "thực hành tốt nhất". Điều đó không có nghĩa là bạn nên làm điều đó một cách giáo điều hoặc mù quáng. Nếu bạn nghĩ rằng bạn nên kiểm tra các phương thức riêng tư của mình và có lý do chính đáng (như bạn đang viết các bài kiểm tra đặc tính cho một ứng dụng cũ), thì hãy kiểm tra các phương thức riêng tư của bạn . Hoàn cảnh cụ thể luôn thổi phồng bất kỳ quy tắc chung hoặc thực hành tốt nhất. Chỉ cần lưu ý một số điều có thể đi sai (xem ở trên).

Tôi có một câu trả lời chi tiết về vấn đề này trên SO mà tôi sẽ không nhắc lại ở đây: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015


4
Lý do 1: mơ hồ. Lý do 2: Điều gì xảy ra nếu phương thức trợ giúp riêng của bạn không phải là một phần của API công khai? Lý do 3: Không nếu bạn thiết kế lớp học của bạn đúng cách. Lời khuyên cuối cùng của bạn: tại sao tôi lại xóa một bài kiểm tra hoàn toàn tốt chứng minh rằng một phương pháp tôi đã viết có hiệu quả?
Robert Harvey

4
@RobertHarvey Lý do 2: có thể truy cập gián tiếp thông qua API công khai! = Là một phần của API công khai. Nếu chức năng riêng tư của bạn không thể kiểm tra được thông qua API công khai, thì có lẽ đó là mã chết và cần được xóa? Hoặc lớp học của bạn thực sự là một tảng băng trôi (lý do 1) và nên được tái cấu trúc.
Frax

5
@RobertHarvey nếu bạn không thể kiểm tra chức năng riêng tư thông qua API công khai thì hãy xóa nó vì nó không phục vụ mục đích hữu ích.
David Arno

1
@RobertHarvey 1: Mùi thiết kế luôn có phần chủ quan / mơ hồ, nên chắc chắn. Nhưng tôi đã liệt kê một số ví dụ cụ thể về các mẫu chống, và có nhiều chi tiết hơn trong câu trả lời SO của tôi. 2: Các phương thức riêng tư không thể là một phần của API công khai (theo định nghĩa: chúng là riêng tư) ... vì vậy tôi không nghĩ câu hỏi của bạn có ý nghĩa nhiều. Tôi đang cố gắng hiểu rằng nếu bạn có một cái gì đó như bin_search(arr, item)(công khai) và bin_search(arr, item, low, hi)(riêng tư, có nhiều cách để thực hiện tìm kiếm bin), thì tất cả những gì bạn cần kiểm tra là công khai đối mặt với một ( bin_search(arr, item))
Matt Messersmith

1
@RobertHarvey 3: Thứ nhất, tôi nói rủi ro , không bảo đảm . Thứ hai, tuyên bố "nó hoạt động nếu bạn làm đúng" là tự hoàn thành. Ví dụ: "Bạn có thể viết một hệ điều hành trong một chức năng duy nhất nếu bạn thực hiện đúng ". Điều này không sai: nhưng nó cũng không hữu ích. Về mẹo: Bạn sẽ xóa nó vì nếu việc triển khai của bạn thay đổi (nghĩa là bạn muốn trao đổi một ẩn riêng tư), thì bộ kiểm tra của bạn sẽ cản trở bạn (bạn sẽ có một bài kiểm tra thất bại ở nơi bạn không nên làm).
Matt Messersmith

13

Cho rằng một trong những mục đích chính của kiểm tra đơn vị là bạn có thể cấu trúc lại các phần bên trong của chương trình và sau đó có thể xác minh rằng bạn đã không phá vỡ chức năng của nó, điều đó sẽ phản tác dụng nếu các bài kiểm tra đơn vị của bạn hoạt động ở mức độ chi tiết tốt đến mức bất kỳ thay đổi nào đối với mã chương trình đều yêu cầu bạn viết lại các bài kiểm tra của mình.


Không chắc chắn tại sao câu trả lời của bạn đã bị hạ cấp. Thật ngắn gọn, đến mức và nhận được câu trả lời đúng 100%.
David Arno

3
@DavidArno: Có thể vì thử nghiệm các phương thức riêng tư thực sự không liên quan nhiều đến độ chi tiết thử nghiệm. Nó có mọi thứ để làm với khớp nối với các chi tiết thực hiện.
Robert Harvey

10

Viết bài kiểm tra đơn vị cho các phương thức riêng tư liên kết các bài kiểm tra đơn vị của bạn với các chi tiết thực hiện.

Các bài kiểm tra đơn vị nên kiểm tra hành vi của một lớp ở bề mặt ngoài của lớp (đó là API công khai). Các bài kiểm tra đơn vị không cần phải biết bất cứ điều gì về bộ phận của một lớp. Viết bài kiểm tra đơn vị đối với các chi tiết triển khai của một lớp ràng buộc bàn tay của bạn khi đến lúc phải cấu trúc lại. Tái cấu trúc gần như chắc chắn sẽ phá vỡ các thử nghiệm đó, vì chúng không phải là một phần của API ổn định của bạn.

Điều đó nói rằng, tại sao bạn có thể muốn viết bài kiểm tra đơn vị cho các phương thức riêng tư của bạn?

Có một sự căng thẳng tự nhiên giữa các bài kiểm tra đơn vị và sự phát triển gia tăng. Các nhà phát triển phần mềm sử dụng REPL (vòng lặp đọc-in) có thể chứng thực mức độ hiệu quả của việc viết và kiểm tra các bit chức năng nhỏ khi bạn "phát triển" một lớp hoặc chức năng. Cách tốt duy nhất để làm điều đó trong các môi trường được điều khiển theo đơn vị là viết các thử nghiệm đơn vị cho các phương thức riêng tư, nhưng có rất nhiều ma sát khi thực hiện điều đó. Các bài kiểm tra đơn vị cần có thời gian để viết, bạn cần một phương pháp thực tế để kiểm tra và khung kiểm tra của bạn cần hỗ trợ khả năng giữ phương thức riêng tư để nó không gây ô nhiễm API bên ngoài của bạn.

Một số hệ sinh thái như C # và .NET có cách tạo môi trường giống REPL (các công cụ như Linqpad làm điều này), nhưng tiện ích của chúng bị hạn chế do bạn không có quyền truy cập vào dự án của mình. Cửa sổ ngay lập tức trong Visual Studio là bất tiện; nó vẫn không có Intellisense đầy đủ, bạn phải sử dụng tên đủ điều kiện trong đó và nó kích hoạt bản dựng mỗi khi bạn sử dụng nó.


4
@ user949300 Thật khó để tranh luận về vấn đề này mà không rơi vào sai lầm ngụy biện của người Scotland thực sự , nhưng có rất nhiều bài kiểm tra viết kém các loại. Từ góc độ thử nghiệm đơn vị, bạn nên kiểm tra hợp đồng công khai về phương pháp của mình mà không cần biết chi tiết triển khai nội bộ. Không phải là khẳng định rằng một sự phụ thuộc nhất định đã được gọi là X lần luôn luôn sai: có những tình huống điều này có ý nghĩa. Bạn chỉ cần đảm bảo rằng đây là thông tin bạn thực sự muốn truyền đạt trong hợp đồng của đơn vị đó đang được thử nghiệm.
Vincent Savard

3
@DavidArno: [nhún vai] Tôi đã làm điều này được một lúc rồi. Các thử nghiệm đơn vị cho các phương thức riêng tư luôn hoạt động tốt đối với tôi, cho đến khi Microsoft quyết định ngừng hỗ trợ các đối tượng proxy trong khung thử nghiệm của chúng. Kết quả là không có gì bùng nổ. Tôi không bao giờ xé toạc một lỗ hổng trong vũ trụ bằng cách viết một bài kiểm tra cho một phương pháp riêng tư.
Robert Harvey

2
@DavidArno: Tại sao tôi lại bỏ việc sử dụng một kỹ thuật hoàn toàn tốt mang lại lợi ích cho tôi, chỉ vì ai đó trên internet nói rằng đó là một ý tưởng tồi mà không cung cấp bất kỳ lời biện minh nào?
Robert Harvey

2
Lợi ích chính mà tôi có được từ các bài kiểm tra đơn vị là cung cấp cho tôi một "mạng lưới an toàn" cho phép tôi sửa lại mã của mình và tự tin khi biết những thay đổi của mình không đưa ra hồi quy. Cuối cùng, việc thử nghiệm các phương thức trợ giúp riêng giúp dễ dàng tìm thấy bất kỳ hồi quy nào như vậy. Khi tôi cấu trúc lại một phương thức trợ giúp riêng và đưa ra lỗi logic, tôi ngắt các kiểm tra cụ thể cho phương thức riêng đó. Nếu các bài kiểm tra đơn vị của tôi tổng quát hơn và chỉ kiểm tra giao diện của đơn vị mã đó, thì vấn đề sẽ khó hiểu hơn nhiều.
Alexander - Phục hồi Monica

2
@Frax Chắc chắn, tôi có thể, nhưng theo logic đó, tôi nên từ bỏ các bài kiểm tra đơn vị để ủng hộ các bài kiểm tra tích hợp trên toàn hệ thống. Xét cho cùng, "trong hầu hết các trường hợp, bạn sẽ có thể sửa đổi các thử nghiệm này để kiểm tra hành vi tương tự"
Alexander - Phục hồi Monica

6

Từ kinh nghiệm của tôi, tôi đã thấy rằng đơn vị kiểm tra các lớp bên trong, các phương thức thường có nghĩa là tôi phải lấy các hàm, các lớp được kiểm tra ra. Để tạo một mức độ trừu tượng khác.

Điều này dẫn đến việc tuân thủ tốt hơn Nguyên tắc Trách nhiệm duy nhất.


Đây phải là câu trả lời được chấp nhận.
Jack Aidley

0

Tôi nghĩ rằng đây là một câu hỏi hay vì nó phơi bày một vấn đề phổ biến trong thử nghiệm bảo hiểm. Nhưng một câu trả lời tốt sẽ cho bạn biết rằng câu hỏi không chính xác bởi vì về lý thuyết, bạn không thể kiểm tra các phương thức riêng tư . Đó là lý do tại sao họ là riêng tư .

Có lẽ một câu hỏi tốt hơn sẽ là "Tôi nên làm gì khi tôi muốn thử nghiệm các phương pháp riêng tư?" và câu trả lời rất rõ ràng: bạn nên phơi bày chúng theo cách có thể giúp kiểm tra. Bây giờ, điều này không nhất thiết có nghĩa là bạn chỉ nên công khai phương thức và đó là phương pháp. Nhiều khả năng bạn sẽ muốn làm trừu tượng cao hơn; chuyển đến một thư viện hoặc API khác để bạn có thể thực hiện các thử nghiệm của mình trên thư viện đó mà không để lộ chức năng đó trong API chính của bạn.

Hãy nhớ rằng có một lý do tại sao các phương thức của bạn có các mức truy cập khác nhau và bạn nên luôn luôn nghĩ về việc cuối cùng các lớp của bạn sẽ được sử dụng như thế nào.



Tôi đã cố gắng giải quyết vấn đề này trong câu hỏi của mình bằng cách nói rằng Python không có khái niệm thực sự về quyền riêng tư và các phương thức ẩn chỉ đơn giản là không được liệt kê nhưng có thể được sử dụng khi bạn biết tên của chúng
Serge Ballesta
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.