Đôi khi các chức năng riêng tư chỉ đơn giản là các đơn vị chức năng nội bộ chưa được trích xuất. Vậy tại sao không kiểm tra chúng?


9

Đôi khi các chức năng riêng tư của một mô-đun hoặc lớp chỉ đơn giản là các đơn vị chức năng nội bộ chưa được trích xuất, có thể xứng đáng được thử nghiệm riêng. Vậy tại sao không kiểm tra chúng? Chúng tôi sẽ viết bài kiểm tra cho chúng sau này nếu / khi chúng được giải nén. Vậy tại sao không viết các bài kiểm tra ngay bây giờ, khi chúng vẫn là một phần của cùng một tệp?

Để lam sang tỏ:

nhập mô tả hình ảnh ở đây

Đầu tiên, tôi đã viết module_a. Bây giờ tôi muốn viết bài kiểm tra cho nó. Tôi muốn kiểm tra chức năng 'riêng tư' _private_func. Tôi không hiểu tại sao tôi sẽ không viết một bài kiểm tra cho nó, nếu sau này tôi có thể cấu trúc lại nó thành mô-đun bên trong của chính nó, và sau đó viết các bài kiểm tra cho nó.


Giả sử tôi có một mô-đun với các chức năng sau (nó cũng có thể là một lớp):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuff_do_more_stufflà các chức năng 'riêng tư' của mô-đun.

Tôi hiểu ý tưởng rằng chúng ta chỉ nên kiểm tra giao diện công cộng, không phải chi tiết triển khai. Tuy nhiên, đây là điều:

_do_stuff_do_more_stuffchứa phần lớn các chức năng của mô-đun. Mỗi một trong số chúng có thể là một chức năng công khai của một mô-đun 'nội bộ' khác nhau. Nhưng chúng chưa được phát triển và đủ lớn để được trích xuất thành các tệp riêng biệt.

Vì vậy, kiểm tra các chức năng này cảm thấy đúng bởi vì chúng là đơn vị quan trọng của chức năng. Nếu chúng ở các mô-đun khác nhau như các chức năng công cộng, chúng tôi sẽ kiểm tra chúng. Vậy tại sao không kiểm tra chúng khi chúng chưa (hoặc chưa bao giờ) được trích xuất sang một tệp khác?



2
"Các phương thức riêng tư có lợi cho thử nghiệm đơn vị ..." "... gắn bó với phương pháp riêng mang lại cho tôi một cải tiến hữu ích, đáng tin cậy trong các thử nghiệm đơn vị. Ngược lại, làm suy yếu các giới hạn truy cập" để kiểm tra "chỉ mang lại cho tôi một sự khó hiểu, khó hiểu một đoạn mã thử nghiệm, cũng có nguy cơ bị phá vỡ vĩnh viễn bởi bất kỳ sự tái cấu trúc nhỏ nào, thật lòng mà nói tôi có vẻ nghi ngờ như nợ kỹ thuật "
gnat

3
"Tôi có nên kiểm tra các chức năng riêng tư không?" Không bao giờ, không bao giờ, bao giờ.
David Arno

2
@DavidArno Tại sao? Cái gì thay thế để kiểm tra nội bộ? Chỉ kiểm tra tích hợp? Hoặc làm cho nhiều thứ công khai hơn? (mặc dù theo kinh nghiệm của tôi, tôi chủ yếu thử nghiệm các phương thức công khai trên các lớp bên trong, chứ không phải các phương thức riêng tư)
CodeInChaos

1
Nếu nó đủ quan trọng để có nhu cầu viết bài kiểm tra cho nó, thì nó đã có sẵn trong một mô-đun riêng biệt. Nếu không, thì bạn kiểm tra hành vi của nó bằng cách sử dụng API công khai.
Vincent Savard

Câu trả lời:


14

Nhu cầu kiểm tra không giống như nhu cầu công khai.

Mã không tầm thường cần thử nghiệm bất kể tiếp xúc. Hành vi không công khai không cần phải tồn tại, hãy để một mình được kiểm tra.

Những quan điểm mâu thuẫn này có thể khiến bạn muốn công khai mọi chức năng hoặc từ chối đưa mã ra thành một chức năng trừ khi nó được công khai.

Đây không phải là câu trả lời. Sẵn sàng tạo các chức năng trợ giúp riêng. Kiểm tra chúng thông qua giao diện công cộng sử dụng nó càng nhiều càng tốt.

Nếu kiểm tra thông qua giao diện công cộng không thực hiện chức năng riêng tư thì chức năng riêng tư đang cố gắng cho phép nhiều.

Xác nhận có thể giúp thu hẹp những gì chức năng riêng tư cho phép. Nếu bạn không thể vượt qua null thông qua giao diện công cộng, bạn vẫn có thể ném ngoại lệ nếu có thông qua.

Tại sao bạn nên Tại sao kiểm tra những gì bạn sẽ không bao giờ nhìn thấy? Vì mọi thứ thay đổi. Nó có thể là riêng tư bây giờ nhưng được công khai sau. Mã gọi có thể thay đổi. Mã từ chối rõ ràng null làm cho việc sử dụng đúng và trạng thái mong đợi rõ ràng.

Tất nhiên null có thể ổn. Nó chỉ là một ví dụ ở đây. Nhưng nếu bạn mong đợi một cái gì đó, nó hữu ích làm cho kỳ vọng đó rõ ràng.

Đó có thể không phải là loại thử nghiệm bạn có trong đầu nhưng hy vọng bạn sẽ sẵn sàng tạo các chức năng trợ giúp riêng khi thích hợp.

Mong muốn thử nghiệm là tốt nhưng nó không phải là động lực trong thiết kế API công khai của bạn. Thiết kế API công khai để dễ sử dụng. Nó có thể sẽ không được nếu mọi chức năng là công khai. API phải là thứ mà mọi người có thể hiểu cách sử dụng mà không cần đi sâu vào mã. Đừng để những người như vậy tự hỏi chức năng của người trợ giúp kỳ lạ này là để làm gì.

Ẩn các chức năng của trình trợ giúp công cộng trong một mô-đun nội bộ là một nỗ lực nhằm tôn trọng nhu cầu về API sạch trong khi phơi bày các trình trợ giúp để kiểm tra. Tôi sẽ không nói điều này là sai. Bạn có thể đang thực hiện bước đầu tiên hướng tới một lớp kiến ​​trúc khác. Nhưng xin vui lòng, làm chủ nghệ thuật kiểm tra các chức năng của người trợ giúp riêng thông qua các chức năng công khai sử dụng chúng trước tiên. Bằng cách đó, bạn sẽ không sử dụng cách giải quyết này.


Tôi đã đưa ra một cách tiếp cận, tôi muốn nghe ý kiến ​​của bạn: bất cứ khi nào tôi ở trong tình huống tôi muốn kiểm tra một chức năng riêng tư, tôi sẽ kiểm tra xem tôi có thể kiểm tra đủ thông qua một trong các chức năng công cộng không (bao gồm tất cả các trường hợp cạnh, vv). Nếu tôi có thể, tôi sẽ không viết một bài kiểm tra cho chức năng này một cách cụ thể, mà chỉ kiểm tra nó thông qua kiểm tra các chức năng công cộng sử dụng nó. Tuy nhiên, nếu tôi cảm thấy chức năng không thể được kiểm tra đầy đủ thông qua giao diện công cộng và xứng đáng được thử nghiệm riêng, tôi sẽ trích xuất nó sang một mô-đun nội bộ nơi công khai và viết thử nghiệm cho nó. Bạn nghĩ sao?
Manila Cohn

Tôi sẽ nói rằng tôi đang hỏi điều tương tự mà những người khác đã trả lời ở đây :) Tôi muốn nghe ý kiến ​​của mọi người.
Manila Cohn

Một lần nữa, tôi sẽ không nói với bạn là không. Tôi lo ngại rằng bạn đã không nói gì về việc để mắt đến việc điều đó ảnh hưởng đến khả năng sử dụng như thế nào. Sự khác biệt giữa công và tư không phải là cấu trúc. Đó là cách sử dụng. Nếu sự khác biệt giữa công và tư là cửa trước và cửa sau thì công việc của bạn là xây dựng nhà kho ở sân sau. Khỏe. Miễn là mọi người không bị lạc trở lại đó.
candied_orange

1
Được nâng cấp cho "Nếu kiểm tra qua giao diện công cộng không thực hiện chức năng riêng tư đủ thì chức năng riêng tư đang cố gắng cho phép nhiều."
Kris Welsh

7

Câu trả lời ngắn gọn: Không

Câu trả lời dài hơn: Có, nhưng thông qua 'API' công khai của lớp bạn

Toàn bộ ý tưởng của các thành viên riêng của một lớp là họ đại diện cho chức năng không thể nhìn thấy bên ngoài 'đơn vị' mã, tuy nhiên, bạn muốn xác định đơn vị đó là gì. Trong mã hướng đối tượng, đơn vị thường kết thúc là một lớp.

Bạn nên thiết kế lớp của mình sao cho có thể gọi tất cả các chức năng riêng tư thông qua các kết hợp trạng thái đầu vào khác nhau. Nếu bạn thấy không có cách nào tương đối đơn giản để làm điều này, có lẽ gợi ý rằng thiết kế của bạn cần được chú ý kỹ hơn.


Sau khi làm rõ câu hỏi, đây chỉ là vấn đề ngữ nghĩa. Nếu mã được đề cập có thể hoạt động như một đơn vị độc lập riêng biệt và đang được thử nghiệm như thể đó là mã công khai, tôi không thể thấy bất kỳ lợi ích nào của việc không chuyển nó thành một mô-đun độc lập. Hiện tại, nó chỉ phục vụ để gây nhầm lẫn cho các nhà phát triển trong tương lai (bao gồm cả chính bạn, trong thời gian 6 tháng), về lý do tại sao mã công khai rõ ràng bị ẩn bên trong một mô-đun khác.


Xin chào, cảm ơn câu trả lời của bạn :) Vui lòng đọc lại câu hỏi, tôi đã chỉnh sửa để làm rõ.
Manila Cohn

Tôi đã đưa ra một cách tiếp cận, tôi muốn nghe ý kiến ​​của bạn: bất cứ khi nào tôi ở trong tình huống tôi muốn kiểm tra một chức năng riêng tư, tôi sẽ kiểm tra xem tôi có thể kiểm tra đủ thông qua một trong các chức năng công cộng không (bao gồm tất cả các trường hợp cạnh, vv). Nếu tôi có thể, tôi sẽ không viết một bài kiểm tra cho chức năng này một cách cụ thể, mà chỉ kiểm tra nó thông qua kiểm tra các chức năng công cộng sử dụng nó. Tuy nhiên, nếu tôi cảm thấy chức năng không thể được kiểm tra đầy đủ thông qua giao diện công cộng và xứng đáng được thử nghiệm riêng, tôi sẽ trích xuất nó sang một mô-đun nội bộ nơi công khai và viết thử nghiệm cho nó. Bạn nghĩ sao?
Manila Cohn

Tôi sẽ nói rằng tôi đang hỏi điều tương tự mà những người khác đã trả lời ở đây :) Tôi muốn nghe ý kiến ​​của mọi người.
Manila Cohn

5

Điểm chung của các chức năng riêng là chúng là các chi tiết triển khai ẩn có thể được thay đổi theo ý muốn, mà không thay đổi API công khai. Đối với mã ví dụ của bạn:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

Nếu bạn có một loạt các bài kiểm tra chỉ sử dụng public_func, thì nếu bạn viết lại thành:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

sau đó, miễn là kết quả trả về cho một giá trị cụ thể avẫn giữ nguyên, thì tất cả các thử nghiệm của bạn sẽ tốt. Nếu kết quả trả về thay đổi, một bài kiểm tra sẽ thất bại, nêu bật thực tế là một cái gì đó đã phá vỡ.

Đây là tất cả một điều tốt: API công cộng tĩnh; cũng đóng gói hoạt động bên trong; và các bài kiểm tra mạnh mẽ.

Tuy nhiên, nếu bạn đã viết các bài kiểm tra cho _do_stuffhoặc _do_more_stuffsau đó thực hiện thay đổi ở trên, thì bây giờ bạn có một loạt các bài kiểm tra bị hỏng, không phải vì chức năng đã thay đổi mà vì việc triển khai chức năng đó đã thay đổi. Các thử nghiệm đó sẽ cần viết lại để hoạt động với các chức năng mới, nhưng khi chúng hoạt động, tất cả những gì bạn biết là các thử nghiệm đó hoạt động với các chức năng mới. Bạn đã mất các bài kiểm tra ban đầu và vì vậy sẽ không biết liệu hành vi của public_funcđã thay đổi hay không và do đó, các bài kiểm tra của bạn về cơ bản sẽ vô dụng.

Đây là một điều xấu: một API thay đổi; tiếp xúc hoạt động bên trong kết hợp chặt chẽ với các bài kiểm tra; và các thử nghiệm dễ vỡ thay đổi ngay khi thay đổi thực hiện được thực hiện.

Vì vậy, không, không kiểm tra chức năng riêng tư. Không bao giờ.


Xin chào David, cảm ơn câu trả lời của bạn. Vui lòng đọc lại câu hỏi của tôi, tôi đã chỉnh sửa để làm rõ.
Manila Cohn

Meh. Không có gì sai khi kiểm tra các chức năng nội bộ. Cá nhân tôi không viết một hàm không tầm thường dưới bất kỳ hình thức nào trừ khi tôi có thể bao quát nó bằng một vài bài kiểm tra đơn vị, vì vậy việc cấm kiểm tra các chức năng bên trong sẽ khiến tôi không thể viết được các chức năng bên trong, cướp đi một kỹ thuật rất hữu ích. API có thể và thay đổi; Khi họ làm, bạn phải thay đổi các bài kiểm tra. Tái cấu trúc một chức năng bên trong (và thử nghiệm của nó) không phá vỡ các thử nghiệm của chức năng bên ngoài; đó là toàn bộ quan điểm của những thử nghiệm đó. Lời khuyên tồi tổng thể.
Robert Harvey

Điều bạn thực sự tranh cãi là bạn không nên có chức năng riêng tư.
Robert Harvey

1
@AvivCohn Chúng đủ lớn để đảm bảo thử nghiệm, trong trường hợp chúng đủ lớn để có được tệp của riêng chúng; hoặc chúng đủ nhỏ để bạn không cần kiểm tra chúng một cách riêng biệt. Vậy đó là cái gì?
Doval

3
@RobertHarvey Không, nó làm cho đối số "chia nhỏ các lớp lớn thành các thành phần được ghép lỏng lẻo nếu cần thiết". Nếu họ thực sự không công khai thì đó có lẽ là trường hợp sử dụng tốt cho khả năng hiển thị riêng tư trọn gói.
Doval
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.