Tôi nên thử nghiệm các phương pháp riêng tư hay chỉ những phương thức công khai? [đóng cửa]


347

Tôi đã đọc bài viết này về cách kiểm tra các phương pháp riêng tư. Tôi thường không kiểm tra chúng, bởi vì tôi luôn nghĩ rằng chỉ nhanh hơn khi kiểm tra các phương thức công khai sẽ được gọi từ bên ngoài đối tượng. Bạn có thử nghiệm phương pháp riêng tư? Tôi có nên luôn luôn kiểm tra chúng?



"Tôi có nên kiểm tra người giúp việc tư nhân?" Đúng. "Tôi có nên kiểm tra người giúp việc tư trực tiếp không?" Nói chung, nếu bạn có thể kiểm tra chúng dễ dàng thông qua giao diện công cộng, tại sao phải kiểm tra chúng trực tiếp? Nếu nó trở nên phức tạp để kiểm tra tất cả các khía cạnh của người trợ giúp thông qua giao diện công cộng, thì thành phần đó có tồn tại lâu hơn sự tồn tại của nó như một đơn vị không?
Mihai Danila

Câu trả lời:


328

Tôi không đơn vị thử nghiệm phương pháp riêng tư. Một phương thức riêng là một chi tiết triển khai cần được ẩn cho người dùng của lớp. Thử nghiệm phương pháp riêng phá vỡ đóng gói.

Nếu tôi thấy rằng phương thức riêng là rất lớn hoặc phức tạp hoặc đủ quan trọng để yêu cầu các thử nghiệm riêng của nó, tôi chỉ cần đặt nó vào một lớp khác và đặt nó ở chế độ công khai ở đó ( Đối tượng phương thức ). Sau đó, tôi có thể dễ dàng kiểm tra phương thức trước đây riêng tư nhưng hiện tại công khai, hiện đang sống trên lớp của chính nó.


88
Tôi không đồng ý. Lý tưởng nhất là bạn viết một bài kiểm tra nhanh trước khi bắt đầu viết mã. Hãy nghĩ về đầu vào điển hình và đầu ra sẽ là gì. Viết bài kiểm tra (không nên mất nhiều thời gian hơn vài giây) và viết mã cho đến khi bài kiểm tra đúng. Không có lý do để từ bỏ phong cách làm việc đó cho các phương pháp riêng tư.
Frank

254
Nói rằng các phương pháp riêng tư không cần thử nghiệm cũng giống như nói một chiếc xe vẫn ổn miễn là nó lái ổn, và không có vấn đề gì dưới mui xe. Nhưng sẽ thật tuyệt khi biết rằng một số cáp bên trong đang bắt đầu bị lỏng - ngay cả khi người dùng không nhận thấy gì? Chắc chắn, bạn có thể làm cho mọi thứ công khai, nhưng vấn đề là gì? Bạn sẽ luôn muốn một số phương pháp riêng tư.
Frank

37
"Một phương thức riêng là một chi tiết triển khai cần được ẩn cho người dùng của lớp." nhưng các thử nghiệm có thực sự ở cùng một phía của giao diện của lớp với người dùng "thông thường" (thời gian chạy) không? ;)
mlvljr

34
Nguy cơ của việc rút ra bất cứ thứ gì bạn muốn kiểm tra vào một lớp khác là bạn có thể kết thúc với chi phí quá cao của việc chế tạo quá mức sản phẩm của mình và có một triệu thành phần có thể tái sử dụng không bao giờ được sử dụng lại.
thỉnh thoảng

44
So sánh một đoạn mã với một chiếc xe hơi là sai; mã không ' xấu đi ' theo thời gian, nó là vĩnh cửu . Nếu việc kiểm tra giao diện công cộng của bạn chỉ đi xa để xác định rằng nó 'có vẻ ổn ' thì việc kiểm tra mã công khai của bạn là không đủ. Trong trường hợp này, thử nghiệm các phương thức riêng tư sẽ không làm cho thử nghiệm tổng thể hoàn thành cho dù bạn có cố gắng thế nào. Tập trung vào kiểm tra toàn diện mã công khai của bạn nói chung, sử dụng kiến ​​thức về hoạt động nội bộ của mã để tạo ra các kịch bản phù hợp.
rustyx

293

Mục đích của thử nghiệm là gì?

Phần lớn các câu trả lời cho đến nay đều nói rằng các phương thức riêng tư là các chi tiết triển khai mà không (hoặc ít nhất là không nên) quan trọng miễn là giao diện công cộng được kiểm tra và hoạt động tốt. Điều đó hoàn toàn chính xác nếu mục đích thử nghiệm duy nhất của bạn là đảm bảo rằng giao diện công cộng hoạt động .

Cá nhân, việc sử dụng chính của tôi để kiểm tra mã là để đảm bảo rằng các thay đổi mã trong tương lai không gây ra sự cố và hỗ trợ các nỗ lực sửa lỗi của tôi nếu chúng xảy ra. Tôi thấy rằng việc thử nghiệm các phương thức riêng tư cũng giống như giao diện công cộng (nếu không muốn nói là như vậy!) Mục đích đó.

Xem xét: Bạn có phương thức công khai A gọi phương thức riêng B. Cả A và B đều sử dụng phương thức C. C bị thay đổi (có thể do bạn, có lẽ bởi nhà cung cấp), khiến A bắt đầu không thử nghiệm. Sẽ không hữu ích khi làm các xét nghiệm cho B, mặc dù nó là riêng tư, để bạn biết liệu vấn đề là do việc sử dụng C, B của C hay cả hai?

Kiểm tra các phương thức riêng tư cũng thêm giá trị trong trường hợp phạm vi kiểm tra của giao diện công cộng không đầy đủ. Mặc dù đây là tình huống chúng tôi thường muốn tránh, thử nghiệm đơn vị hiệu quả phụ thuộc cả vào các thử nghiệm tìm lỗi và chi phí phát triển và bảo trì liên quan của các thử nghiệm đó. Trong một số trường hợp, lợi ích của phạm vi kiểm tra 100% có thể được đánh giá là không đủ để đảm bảo chi phí cho các thử nghiệm đó, tạo ra các lỗ hổng trong phạm vi kiểm tra của giao diện công cộng. Trong các trường hợp như vậy, một thử nghiệm được nhắm mục tiêu tốt của một phương thức riêng tư có thể là một bổ sung rất hiệu quả cho cơ sở mã.


72
Vấn đề ở đây là "những thay đổi mã trong tương lai" luôn có nghĩa là tái cấu trúc hoạt động bên trong của một số lớp. Điều này xảy ra thường xuyên đến nỗi các bài kiểm tra viết tạo ra một rào cản để tái cấu trúc.
Lập trình viên ngoài vòng pháp luật

40
Ngoài ra, nếu bạn liên tục thay đổi các bài kiểm tra đơn vị của mình thì bạn đã mất tất cả tính nhất quán trong kiểm tra của mình và thậm chí bạn sẽ có khả năng tự tạo ra các lỗi trong các bài kiểm tra đơn vị.
17 trên 26 tháng

6
@ 17 Nếu các kiểm tra và thực hiện được sửa đổi đồng bộ (như, có vẻ như nó nên như vậy), sẽ có ít vấn đề hơn.
mlvljr

11
@Sauronlord, Lý do bạn kiểm tra các phương thức riêng tư là vì nếu bạn chỉ kiểm tra các phương thức công khai, khi kiểm tra thất bại, chúng tôi không biết trực tiếp nguyên nhân gốc của lỗi đó là do đâu. Nó có thể ở một trong hai testDoSomething()hoặc testDoSomethingPrivate(). Điều này làm cho bài kiểm tra ít giá trị hơn. . Dưới đây là những lý do để thử nghiệm riêng stackoverflow.com/questions/34571/... :
Pacerier

3
@Pacerier Cũng có một sự khác biệt giữa kiểm tra mã của bạn và có quy trình kiểm tra tự động liên tục. Rõ ràng bạn nên đảm bảo phương thức riêng tư của mình hoạt động, nhưng bạn không nên kiểm tra khớp bạn với phương thức riêng tư, vì nó không phải là một phần của trường hợp sử dụng phần mềm.
Didier A.

150

Tôi có xu hướng làm theo lời khuyên của Dave Thomas và Andy Hunt trong cuốn sách Thử nghiệm đơn vị thực dụng của họ :

Nói chung, bạn không muốn phá vỡ bất kỳ đóng gói nào vì mục đích thử nghiệm (hoặc như mẹ thường nói, "đừng để lộ thông tin cá nhân của bạn!"). Hầu hết thời gian, bạn sẽ có thể kiểm tra một lớp bằng cách thực hiện các phương thức công khai của nó. Nếu có chức năng quan trọng được ẩn đằng sau quyền truy cập riêng tư hoặc được bảo vệ, đó có thể là dấu hiệu cảnh báo rằng có một lớp khác đang ở đó đấu tranh để thoát ra.

Nhưng đôi khi tôi không thể ngăn bản thân thử nghiệm các phương pháp riêng tư bởi vì nó mang lại cho tôi cảm giác yên tâm rằng tôi đang xây dựng một chương trình hoàn toàn mạnh mẽ.


9
Tôi sẽ khuyên bạn nên vô hiệu hóa các bài kiểm tra đơn vị nhắm mục tiêu các phương pháp riêng tư. Chúng là một khớp nối mã, và sẽ tạo gánh nặng cho công việc tái cấu trúc trong tương lai, hoặc thậm chí đôi khi gây cản trở cho việc bổ sung hoặc sửa đổi tính năng. Thật tốt khi viết một bài kiểm tra cho họ khi bạn đang thực hiện chúng, như một cách tự động để bạn khẳng định bạn đang thực hiện công việc, nhưng không có lợi khi giữ các bài kiểm tra là hồi quy.
Didier A.

61

Tôi cảm thấy bắt buộc phải kiểm tra các chức năng riêng tư vì tôi đang theo dõi ngày càng nhiều một trong những khuyến nghị QA mới nhất của chúng tôi trong dự án của chúng tôi:

Không quá 10 trong độ phức tạp chu kỳ cho mỗi chức năng.

Bây giờ, tác dụng phụ của việc thi hành chính sách này là nhiều chức năng công cộng rất lớn của tôi được chia thành nhiều chức năng riêng tư tập trung hơn, được đặt tên tốt hơn .
Chức năng công cộng vẫn còn đó (tất nhiên) nhưng về cơ bản được giảm xuống để gọi tất cả các 'chức năng phụ' riêng tư đó

Điều đó thực sự thú vị, bởi vì Callstack bây giờ dễ đọc hơn nhiều (thay vì một lỗi trong một chức năng lớn, tôi có một lỗi trong một chức năng phụ với tên của các chức năng trước đó trong callstack để giúp tôi hiểu "Làm thế nào tôi đến đó")

Tuy nhiên, giờ đây việc kiểm tra trực tiếp các chức năng riêng tư đó có vẻ dễ dàng hơn và để việc kiểm tra chức năng công cộng lớn thành một loại thử nghiệm 'tích hợp' trong đó một kịch bản cần được giải quyết.

Chỉ cần 2 xu của tôi.


2
để phản ứng với @jop, tôi không cảm thấy cần phải xuất các hàm riêng tư đó (được tạo ra do sự phân chia của một hàm công khai phức tạp quá lớn) thành một lớp khác. Tôi muốn có chúng vẫn được kết hợp chặt chẽ với chức năng công cộng, trong cùng một lớp. Nhưng vẫn được thử nghiệm đơn vị.
VonC

2
Kinh nghiệm của tôi là những phương thức riêng tư đó chỉ là phương thức tiện ích đang được sử dụng lại bởi những phương thức công khai đó. Đôi khi thuận tiện hơn khi chia lớp ban đầu thành hai (hoặc ba) lớp gắn kết hơn, làm cho các phương thức riêng tư đó được công khai trong các lớp riêng của chúng và do đó có thể kiểm tra được.
jop

7
Không, trong trường hợp của tôi, các hàm riêng mới đó thực sự là một phần của thuật toán lớn hơn được biểu thị bởi hàm công khai. Hàm đó được chia thành các phần nhỏ hơn, không phải là tiện ích, mà là các bước của một quy trình lớn hơn. Do đó, cần phải kiểm tra đơn vị chúng (thay vì kiểm tra đơn vị toàn bộ thuật toán cùng một lúc)
VonC

Đối với những người quan tâm đến độ phức tạp của chu kỳ, tôi đã thêm một câu hỏi về chủ đề: stackoverflow.com/questions/105852/ ích
VonC

Rất tiếc, url của câu hỏi đã thay đổi do lỗi chính tả trong tiêu đề! stackoverflow.com/questions/105852/ cường
VonC

51

Có, tôi kiểm tra các chức năng riêng tư, bởi vì mặc dù chúng được kiểm tra bằng các phương thức công khai của bạn, nhưng thật tuyệt vời trong TDD (Test Driven Design) để kiểm tra phần nhỏ nhất của ứng dụng. Nhưng các chức năng riêng tư không thể truy cập khi bạn ở trong lớp đơn vị thử nghiệm. Đây là những gì chúng tôi làm để kiểm tra các phương pháp riêng tư của chúng tôi.

Tại sao chúng ta có phương pháp riêng?

Các hàm riêng chủ yếu tồn tại trong lớp của chúng tôi vì chúng tôi muốn tạo mã có thể đọc được trong các phương thức công khai của chúng tôi. Chúng tôi không muốn người dùng của lớp này gọi trực tiếp các phương thức này, nhưng thông qua các phương thức công khai của chúng tôi. Ngoài ra, chúng tôi không muốn thay đổi hành vi của họ khi mở rộng lớp (trong trường hợp được bảo vệ), do đó đây là một tư nhân.

Khi chúng tôi viết mã, chúng tôi sử dụng thiết kế dựa trên thử nghiệm (TDD). Điều này có nghĩa là đôi khi chúng ta vấp phải một phần chức năng riêng tư và muốn thử nghiệm. Các hàm riêng tư không thể kiểm tra được trong phpUnit, vì chúng ta không thể truy cập chúng trong lớp Test (chúng là riêng tư).

Chúng tôi nghĩ rằng đây là 3 giải pháp:

1. Bạn có thể kiểm tra tư nhân của mình thông qua các phương thức công khai của bạn

Ưu điểm

  • Kiểm tra đơn vị đơn giản (không cần 'hack')

Nhược điểm

  • Lập trình viên cần hiểu phương thức công khai, trong khi anh ta chỉ muốn thử nghiệm phương thức riêng tư
  • Bạn không kiểm tra phần nhỏ nhất có thể kiểm tra của ứng dụng

2. Nếu riêng tư là rất quan trọng, thì có lẽ đó là một mật mã để tạo một lớp riêng biệt mới cho nó

Ưu điểm

  • Bạn có thể cấu trúc lại lớp này sang một lớp mới, bởi vì nếu nó quan trọng, các lớp khác cũng có thể cần nó
  • Đơn vị có thể kiểm tra bây giờ là một phương thức công khai, vì vậy có thể kiểm tra được

Nhược điểm

  • Bạn không muốn tạo một lớp nếu nó không cần thiết và chỉ được sử dụng bởi lớp có phương thức đến từ
  • Mất hiệu suất tiềm năng vì thêm chi phí

3. Thay đổi công cụ sửa đổi truy cập thành (cuối cùng) được bảo vệ

Ưu điểm

  • Bạn đang kiểm tra phần nhỏ nhất có thể kiểm tra của ứng dụng. Khi sử dụng bảo vệ cuối cùng, chức năng sẽ không bị quá tải (giống như riêng tư)
  • Không mất hiệu suất
  • Không có chi phí phụ

Nhược điểm

  • Bạn đang thay đổi quyền truy cập riêng tư sang được bảo vệ, điều đó có nghĩa là trẻ em có thể truy cập được
  • Bạn vẫn cần một lớp Mock trong lớp thử nghiệm của mình để sử dụng nó

Thí dụ

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

Vì vậy, đơn vị thử nghiệm của chúng tôi bây giờ có thể gọi test_s ngủWithSuspect để kiểm tra chức năng riêng tư trước đây của chúng tôi.


eddy147, tôi thực sự thích khái niệm thử nghiệm các phương thức được bảo vệ thông qua các giả. Cảm ơn!!!!
Theodore R. Smith

15
Tôi chỉ muốn chỉ ra rằng trong mô tả ban đầu về TDD, trong thử nghiệm đơn vị, đơn vịlớp , không phải là một phương thức / hàm. Vì vậy, khi bạn đề cập đến "thử nghiệm phần nhỏ nhất của ứng dụng", thật sai lầm khi coi phần thử nghiệm nhỏ nhất là một phương pháp. Nếu bạn sử dụng logic đó, bạn cũng có thể nói một dòng mã thay vì toàn bộ khối mã.
Matt Quigley

@Matt Một đơn vị công việc có thể trỏ đến một lớp, nhưng cũng là một phương thức duy nhất.
eddy147

4
@ eddy147 Kiểm thử đơn vị đến Kiểm tra hướng phát triển, trong đó đơn vị được xác định là một lớp. Như đã xảy ra với The Internets, ngữ nghĩa đã mở rộng có nghĩa là rất nhiều thứ (nghĩa là hỏi 2 người về sự khác biệt giữa kiểm tra đơn vị và tích hợp là gì, và bạn sẽ nhận được 7 câu trả lời). TDD có nghĩa là một cách để viết phần mềm với các nguyên tắc RẮN, bao gồm cả Trách nhiệm đơn lẻ, trong đó một lớp có một trách nhiệm duy nhất và không nên có độ phức tạp theo chu kỳ cao. Trong TDD, bạn viết cả lớp và kiểm tra cùng nhau, cả hai đơn vị. Các phương thức riêng được gói gọn không có bài kiểm tra đơn vị tương ứng.
Matt Quigley

"Khi chúng tôi viết mã, chúng tôi sử dụng thiết kế dựa trên thử nghiệm (TDD). Điều này có nghĩa là đôi khi chúng tôi vấp phải một phần chức năng riêng tư và muốn thử nghiệm." Tôi hoàn toàn không đồng ý với tuyên bố này, xin vui lòng xem câu trả lời của tôi dưới đây để biết thêm chi tiết. TDD không có nghĩa là bạn buộc phải thử nghiệm các phương pháp riêng tư. Bạn có thể chọn thử nghiệm các phương thức riêng tư: và đó là lựa chọn của bạn, nhưng đó không phải là TDD đang khiến bạn làm điều đó.
Matt Messersmith

41

Tôi không thích thử nghiệm chức năng riêng tư vì một vài lý do. Chúng là như sau (đây là những điểm chính cho người TLDR):

  1. Thông thường khi bạn muốn thử nghiệm phương thức riêng tư của lớp, đó là mùi thiết kế.
  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!

Tôi sẽ giải thích từng điều này bằng một ví dụ cụ thể. Hóa ra 2) và 3) có mối liên hệ khá phức tạp, vì vậy ví dụ của chúng tương tự nhau, mặc dù tôi coi chúng là những lý do riêng biệt tại sao bạn không nên thử nghiệm các phương pháp riêng tư.

Đôi khi thử nghiệm các phương pháp riêng tư là phù hợp, điều quan trọng là phải nhận thức được những nhược điểm được liệt kê ở trên. Tôi sẽ đi qua chi tiết hơn sau.

Tôi cũng đi qua lý do tại sao TDD không phải là một lý do hợp lệ để thử nghiệm các phương thức riêng tư vào cuối.

Tái cấu trúc theo cách của bạn ra khỏi một thiết kế xấu

Một trong những trò chơi phổ biến (chống) mà tôi thấy là những gì Michael Feathers gọi là lớp "Iceberg" (nếu bạn không biết Michael Feathers là ai, hãy mua / đọc cuốn sách "Làm việc hiệu quả với Bộ luật kế thừa". một người đáng để biết nếu bạn là một kỹ sư / nhà phát triển phần mềm chuyên nghiệp). Có những mô hình (chống) khác khiến vấn đề này tăng lên, nhưng đây là mô hình phổ biến nhất mà tôi đã vấp phải. Các lớp "Iceberg" có một phương thức chung và phần còn lại là riêng tư (đó là lý do tại sao nó muốn thử nghiệm các phương thức riêng tư). Nó được gọi là lớp "Iceberg" bởi vì thường có một phương thức công khai đơn độc, nhưng phần còn lại của chức năng được ẩn dưới nước dưới dạng phương thức riêng.

Đánh giá quy tắc

Ví dụ, bạn có thể muốn kiểm tra GetNextToken()bằng cách gọi nó trên một chuỗi liên tiếp và thấy rằng nó trả về kết quả mong đợi. Một chức năng như thế này đảm bảo kiểm tra: hành vi đó không tầm thường, đặc biệt nếu quy tắc mã thông báo của bạn phức tạp. Hãy giả vờ rằng nó không quá phức tạp và chúng tôi chỉ muốn kết nối các mã thông báo được phân định bởi không gian. Vì vậy, bạn viết một bài kiểm tra, có thể nó trông giống như thế này (một số mã psuedo bất khả tri ngôn ngữ, hy vọng ý tưởng này rõ ràng):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Chà, điều đó thực sự trông khá đẹp. Chúng tôi muốn đảm bảo rằng chúng tôi duy trì hành vi này khi chúng tôi thực hiện các thay đổi. Nhưng GetNextToken()là một chức năng riêng tư ! Vì vậy, chúng tôi không thể kiểm tra nó như thế này, vì nó thậm chí không biên dịch (giả sử chúng tôi đang sử dụng một số ngôn ngữ thực sự thực thi công khai / riêng tư, không giống như một số ngôn ngữ kịch bản như Python). Nhưng còn việc thay đổi RuleEvaluatorlớp học để tuân theo Nguyên tắc Trách nhiệm Đơn lẻ (Nguyên tắc Trách nhiệm Đơn lẻ) thì sao? Chẳng hạn, chúng ta dường như có một trình phân tích cú pháp, mã thông báo và trình đánh giá bị kẹt vào một lớp. Sẽ không tốt hơn nếu chỉ tách những trách nhiệm đó? Ngày đầu đó, nếu bạn tạo một Tokenizerlớp, sau đó phương pháp nào nó sẽ là HasMoreTokens()GetNextTokens(). Các RuleEvaluatorlớp có thể có mộtTokenizerđối tượng như một thành viên. Bây giờ, chúng ta có thể giữ bài kiểm tra tương tự như trên, ngoại trừ chúng ta đang kiểm tra Tokenizerlớp thay vì RuleEvaluatorlớp.

Đây là những gì nó có thể trông giống như trong UML:

Đánh giá lại quy tắc

Lưu ý rằng thiết kế mới này làm tăng tính mô đun, do đó bạn có thể sử dụng lại các lớp này trong các phần khác của hệ thống (trước khi bạn không thể, các phương thức riêng tư không thể sử dụng lại theo định nghĩa). Đây là lợi thế chính của việc phá vỡ RuleEvaluator, cùng với sự hiểu biết / địa phương tăng lên.

Thử nghiệm sẽ trông cực kỳ giống nhau, ngoại trừ nó thực sự sẽ biên dịch lần này vì GetNextToken()phương thức này hiện công khai trên Tokenizerlớp:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

Kiểm tra các thành phần riêng thông qua giao diện công cộng và tránh trùng lặp kiểm tra

Ngay cả khi bạn không nghĩ rằng bạn có thể chia nhỏ vấn đề của mình thành các thành phần mô-đun ít hơn (mà bạn có thể 95% thời gian nếu bạn chỉ cố gắng làm điều đó), bạn chỉ có thể kiểm tra các chức năng riêng tư thông qua giao diện công cộng. Nhiều lần các thành viên tư nhân không đáng để thử nghiệm vì họ sẽ được kiểm tra thông qua giao diện công cộng. Nhiều lần những gì tôi thấy là các bài kiểm tra trông rất giống nhau, nhưng kiểm tra hai hàm / phương thức khác nhau. Điều cuối cùng xảy ra là khi các yêu cầu thay đổi (và chúng luôn luôn thay đổi), giờ đây bạn có 2 bài kiểm tra bị hỏng thay vì 1. Và nếu bạn thực sự kiểm tra tất cả các phương pháp riêng tư của mình, bạn có thể có nhiều hơn 10 bài kiểm tra bị hỏng thay vì 1. Tóm lại , kiểm tra các chức năng riêng tư (bằng cách sử dụngFRIEND_TESThoặc đặt chúng ở chế độ công khai hoặc sử dụng sự phản chiếu) có thể được kiểm tra thông qua giao diện chung có thể gây ra sự trùng lặp thử nghiệm . Bạn thực sự không muốn điều này, bởi vì không có gì làm tổn thương nhiều hơn bộ thử nghiệm của bạn làm bạn chậm lại. Nó được cho là để giảm thời gian phát triển và giảm chi phí bảo trì! Nếu bạn kiểm tra các phương thức riêng được kiểm tra theo cách khác thông qua giao diện công cộng, bộ kiểm thử rất có thể làm ngược lại và tích cực tăng chi phí bảo trì và tăng thời gian phát triển. Khi bạn đặt một chức năng riêng tư ở chế độ công khai hoặc nếu bạn sử dụng một cái gì đó như FRIEND_TESTvà / hoặc sự phản chiếu, bạn sẽ thường hối hận về lâu dài.

Xem xét việc thực hiện có thể sau đây của Tokenizerlớp:

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

Giả sử có SplitUpByDelimiter()trách nhiệm trả về một mảng sao cho mỗi phần tử trong mảng là mã thông báo. Hơn nữa, hãy nói rằng đó GetNextToken()chỉ đơn giản là một trình vòng lặp trên vectơ này. Vì vậy, bài kiểm tra công khai của bạn có thể trông như thế này:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Hãy giả vờ rằng chúng ta có thứ mà Michael Feather gọi là công cụ dò dẫm . Đây là một công cụ cho phép bạn chạm vào các bộ phận riêng tư của người khác. Một ví dụ là FRIEND_TESTtừ googletest hoặc phản chiếu nếu ngôn ngữ hỗ trợ nó.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Chà, bây giờ hãy nói rằng các yêu cầu thay đổi và việc token hóa trở nên phức tạp hơn nhiều. Bạn quyết định rằng một dấu phân cách chuỗi đơn giản sẽ không đủ và bạn cần một Delimiterlớp để xử lý công việc. Đương nhiên, bạn sẽ mong đợi một thử nghiệm bị phá vỡ, nhưng nỗi đau đó tăng lên khi bạn kiểm tra các chức năng riêng tư.

Khi nào có thể thử nghiệm phương pháp riêng tư là phù hợp?

Không có "một kích thước phù hợp với tất cả" trong phần mềm. Đôi khi không sao (và thực sự lý tưởng) để "phá vỡ các quy tắc". Tôi mạnh mẽ ủng hộ không kiểm tra chức năng riêng tư khi bạn có thể. Có hai tình huống chính khi tôi nghĩ nó ổn:

  1. Tôi đã làm việc rộng rãi với các hệ thống cũ (đó là lý do tại sao tôi là một fan hâm mộ lớn của Michael Feathers) và tôi có thể nói một cách an toàn rằng đôi khi chỉ đơn giản là an toàn nhất khi chỉ kiểm tra chức năng riêng tư. Nó có thể đặc biệt hữu ích cho việc đưa "các bài kiểm tra đặc tính" vào đường cơ sở.

  2. Bạn đang vội, và phải làm điều nhanh nhất có thể ở đây và bây giờ. Về lâu dài, bạn không muốn thử nghiệm các phương pháp riêng tư. Nhưng tôi sẽ nói rằng thường phải mất một thời gian để cấu trúc lại để giải quyết các vấn đề thiết kế. Và đôi khi bạn phải vận chuyển trong một tuần. Không sao đâu: làm nhanh và bẩn và kiểm tra các phương pháp riêng tư bằng cách sử dụng công cụ dò dẫm nếu đó là cách bạn nghĩ là cách nhanh nhất và đáng tin cậy nhất để hoàn thành công việc. Nhưng hãy hiểu rằng những gì bạn đã làm là không tối ưu trong thời gian dài, và vui lòng xem xét quay lại với nó (hoặc, nếu nó bị quên nhưng bạn sẽ thấy nó sau, hãy sửa nó).

Có lẽ có những tình huống khác mà nó ổn. Nếu bạn nghĩ rằng nó ổn, và bạn có một lời biện minh tốt, thì hãy làm điều đó. Không ai được ngăn cản bạn. Chỉ cần nhận thức được các chi phí tiềm năng.

Lý do TDD

Bên cạnh đó, tôi thực sự không thích mọi người sử dụng TDD như một cái cớ để thử nghiệm các phương pháp riêng tư. Tôi thực hành TDD và tôi không nghĩ TDD buộc bạn phải làm điều này. Bạn có thể viết thử nghiệm của mình (cho giao diện công cộng của bạn) trước, sau đó viết mã để đáp ứng giao diện đó. Đôi khi tôi viết một bài kiểm tra cho giao diện công cộng và tôi cũng sẽ thỏa mãn nó bằng cách viết một hoặc hai phương thức riêng tư nhỏ hơn (nhưng tôi không kiểm tra trực tiếp các phương thức riêng tư, nhưng tôi biết chúng hoạt động hoặc thử nghiệm công khai của tôi sẽ thất bại ). Nếu tôi cần kiểm tra các trường hợp cạnh của phương pháp riêng tư đó, tôi sẽ viết một loạt các bài kiểm tra sẽ đánh chúng qua giao diện công cộng của tôi.Nếu bạn không thể tìm ra cách đánh vào các trường hợp cạnh, đây là một dấu hiệu mạnh bạn cần cấu trúc lại thành các thành phần nhỏ với các phương thức công khai của riêng chúng. Đó là một dấu hiệu các chức năng riêng tư của bạn đang hoạt động quá nhiều và nằm ngoài phạm vi của lớp .

Ngoài ra, đôi khi tôi thấy tôi viết một bài kiểm tra quá lớn để cắn vào lúc này và vì vậy tôi nghĩ rằng "sau này tôi sẽ quay lại bài kiểm tra đó khi tôi có nhiều API hơn để làm việc" (Tôi Tôi sẽ bình luận và giữ nó trong tâm trí tôi). Đây là nơi có rất nhiều nhà phát triển mà tôi đã gặp sau đó sẽ bắt đầu viết bài kiểm tra cho chức năng riêng tư của họ, sử dụng TDD làm vật tế thần. Họ nói "ồ, tôi cần một số thử nghiệm khác, nhưng để viết thử nghiệm đó, tôi sẽ cần các phương thức riêng tư này. Vì vậy, vì tôi không thể viết bất kỳ mã sản xuất nào mà không viết thử nghiệm, tôi cần phải viết thử nghiệm cho một phương pháp riêng tư. " Nhưng những gì họ thực sự cần làm là tái cấu trúc thành các thành phần nhỏ hơn và có thể tái sử dụng thay vì thêm / kiểm tra một loạt các phương thức riêng tư vào lớp hiện tại của họ.

Ghi chú:

Tôi đã trả lời một câu hỏi tương tự về việc thử nghiệm các phương thức riêng tư bằng GoogleTest một lúc trước. Tôi hầu như đã sửa đổi câu trả lời đó thành thuyết bất khả tri ngôn ngữ hơn ở đây.

PS Đây là bài giảng có liên quan về các lớp băng và công cụ mò mẫm của Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU


Vấn đề tôi gặp phải khi liệt kê "bạn có khả năng sử dụng lại các lớp này trong các phần khác trong hệ thống của bạn" là một lợi thế, đôi khi lý do tôi đánh dấu một chức năng là riêng tư là vì tôi không muốn nó được sử dụng bởi các phần khác của hệ thống. Đây là một vấn đề cụ thể về ngôn ngữ: lý tưởng, đây sẽ là riêng tư đối với "mô-đun", nhưng nếu ngôn ngữ không hỗ trợ điều đó (ví dụ PHP), lớp của tôi đại diện cho mô-đun, không phải đơn vị: các phương thức riêng là mã có thể sử dụng lại với các hợp đồng của riêng họ, nhưng chỉ được sử dụng lại trong lớp đó.
IMSoP

Tôi hiểu những gì bạn đang nói, nhưng tôi thích cách cộng đồng Python xử lý vấn đề đó. Nếu bạn đặt tên thành viên "riêng tư" theo câu hỏi _, nó báo hiệu "này, đây là 'riêng tư'. Bạn có thể sử dụng nó, nhưng tiết lộ đầy đủ, nó không được thiết kế để sử dụng lại và bạn chỉ nên sử dụng nó nếu bạn thực sự biết những gì bạn đang làm ". Bạn có thể thực hiện cùng một cách tiếp cận trong bất kỳ ngôn ngữ nào: làm cho các thành viên đó công khai, nhưng đánh dấu họ bằng vị trí dẫn đầu _. Hoặc có lẽ những chức năng đó thực sự nên ở chế độ riêng tư và chỉ được thử nghiệm qua giao diện công cộng (xem câu trả lời để biết thêm chi tiết). Tùy từng trường hợp, không có quy tắc chung
Matt Messersmith

26

Tôi nghĩ tốt nhất là chỉ kiểm tra giao diện chung của một đối tượng. Từ quan điểm của thế giới bên ngoài, chỉ có hành vi của giao diện công cộng mới quan trọng và đây là điều mà các bài kiểm tra đơn vị của bạn nên được hướng tới.

Khi bạn có một số bài kiểm tra đơn vị vững chắc được viết cho một đối tượng mà bạn không muốn phải quay lại và thay đổi các bài kiểm tra đó chỉ vì việc triển khai đằng sau giao diện đã thay đổi. Trong tình huống này, bạn đã phá hỏng tính nhất quán của thử nghiệm đơn vị của bạn.


21

Nếu phương thức riêng tư của bạn không được kiểm tra bằng cách gọi các phương thức công khai của bạn thì nó đang làm gì? Tôi đang nói chuyện riêng tư không được bảo vệ hoặc bạn bè.


3
Cảm ơn bạn. Đây là một nhận xét bị đánh giá thấp đáng ngạc nhiên và đặc biệt vẫn còn có liên quan, thậm chí sau gần 8 năm kể từ khi nó được viết.
Sauronlord

1
Với lý do tương tự, người ta có thể lập luận chỉ kiểm tra phần mềm từ giao diện người dùng (kiểm tra mức hệ thống), bởi vì bằng cách nào đó mọi chức năng trong phần mềm sẽ được thực thi bằng cách nào đó từ đó.
Dirk Herrmann

18

Nếu phương thức riêng được xác định rõ (nghĩa là nó có chức năng có thể kiểm tra được và không có nghĩa là thay đổi theo thời gian) thì có. Tôi kiểm tra tất cả mọi thứ có thể kiểm tra được nơi nó có ý nghĩa.

Chẳng hạn, một thư viện mã hóa có thể che giấu sự thật rằng nó thực hiện mã hóa khối bằng một phương thức riêng chỉ mã hóa 8 byte mỗi lần. Tôi sẽ viết một bài kiểm tra đơn vị cho điều đó - nó không có nghĩa là thay đổi, mặc dù nó bị ẩn và nếu nó bị hỏng (chẳng hạn như do cải tiến hiệu suất trong tương lai) thì tôi muốn biết rằng đó là chức năng riêng tư bị hỏng, không chỉ rằng một trong những chức năng công cộng đã phá vỡ.

Nó tăng tốc độ gỡ lỗi sau.

-Adam


1
Trong trường hợp này, sẽ không có ý nghĩa gì khi chuyển phương thức riêng tư đó sang một lớp khác, sau đó chỉ làm cho nó thành công khai hay tĩnh?
Lập trình viên ngoài vòng pháp luật

+1 Nếu bạn không kiểm tra các chức năng thành viên riêng tư của mình và thử nghiệm giao diện công cộng của bạn không thành công, tất cả những gì bạn sẽ nhận được là kết quả tương đương với thứ gì đó bị hỏng mà không biết đó là thứ gì.
Olumide

12

Nếu bạn đang phát triển hướng kiểm tra (TDD), bạn sẽ kiểm tra các phương thức riêng tư của mình.


2
Bạn sẽ trích xuất các phương thức riêng tư khi tái cấu trúc agXLips.blogspot.com/2008/11/ Khăn
Josh Johnson

4
Không đúng, bạn kiểm tra các phương thức công khai của mình và một khi các bài kiểm tra vượt qua thì bạn trích xuất mã trong các phương thức công khai của mình thành các phương thức riêng để thực hiện bước "làm sạch". Thử nghiệm các phương thức riêng tư là một ý tưởng tồi vì nó làm thay đổi cách thực hiện khó hơn (nếu một ngày nào đó bạn muốn thay đổi cách bạn làm một cái gì đó, bạn sẽ có thể thay đổi nó và chạy tất cả các thử nghiệm của mình và nếu cách làm mới của bạn điều chính xác là họ nên vượt qua, tôi sẽ không muốn phải thay đổi tất cả các bài kiểm tra riêng tư của mình cho việc này).
Tesseract

1
@Tesseract, nếu tôi có thể nâng cao nhận xét của bạn nhiều hơn một lần tôi sẽ làm. "... bạn sẽ có thể thay đổi nó và chạy tất cả các bài kiểm tra của mình và nếu cách làm mới của bạn là đúng thì họ sẽ vượt qua" THAT là một trong những lợi ích chính của bài kiểm tra đơn vị. Chúng cho phép bạn tái cấu trúc với sự tự tin. Bạn hoàn toàn có thể thay đổi hoạt động riêng tư bên trong của lớp và (không cần viết lại tất cả các bài kiểm tra đơn vị của bạn) tự tin rằng bạn đã không phá vỡ bất cứ điều gì vì tất cả các bài kiểm tra đơn vị (hiện tại) của bạn (trên giao diện công cộng) vẫn vượt qua.
Lee

Không đồng ý, hãy xem câu trả lời của tôi dưới đây
Matt Messersmith

11

Tôi không phải là một chuyên gia trong lĩnh vực này, nhưng kiểm thử đơn vị nên kiểm tra hành vi, không thực hiện. Các phương thức riêng tư là một phần của việc triển khai, vì vậy IMHO không nên được kiểm tra.


Trường hợp thực hiện sau đó được thử nghiệm? Nếu một số chức năng sử dụng bộ đệm, đây có phải là chi tiết triển khai và bộ đệm không được kiểm tra không?
Dirk Herrmann

11

Chúng tôi kiểm tra các phương thức riêng tư bằng cách suy luận, theo ý tôi, chúng tôi tìm kiếm tổng phạm vi kiểm tra của lớp ít nhất 95%, nhưng chỉ có các thử nghiệm của chúng tôi gọi vào các phương thức công khai hoặc nội bộ. Để có được phạm vi bảo hiểm, chúng tôi cần thực hiện nhiều cuộc gọi đến công chúng / nội bộ dựa trên các kịch bản khác nhau có thể xảy ra. Điều này làm cho các thử nghiệm của chúng tôi có ý định hơn xung quanh mục đích của mã mà chúng đang thử nghiệm.

Câu trả lời của Trump cho bài đăng bạn liên kết là câu trả lời hay nhất.


9

Các bài kiểm tra đơn vị tôi tin là để thử nghiệm các phương pháp công cộng. Các phương thức công khai của bạn sử dụng các phương thức riêng tư của bạn, do đó, gián tiếp chúng cũng đang được thử nghiệm.


7

Tôi đã cố gắng khắc phục vấn đề này trong một thời gian, đặc biệt là khi thử sức mình với TDD.

Tôi đã bắt gặp hai bài viết mà tôi nghĩ rằng giải quyết vấn đề này đủ kỹ lưỡng trong trường hợp TDD.

  1. Thử nghiệm phương pháp riêng, TDD và Tái cấu trúc dựa trên thử nghiệm
  2. Phát triển dựa trên thử nghiệm không phải là thử nghiệm

Tóm tắt:

  • Khi sử dụng các kỹ thuật phát triển theo hướng kiểm thử (thiết kế), các phương thức riêng tư chỉ nên phát sinh trong quá trình tái bao thanh toán của mã đã làm việc và đã thử nghiệm.

  • Theo bản chất của quy trình, bất kỳ bit nào của chức năng triển khai đơn giản được trích xuất từ ​​chức năng được kiểm tra kỹ lưỡng sẽ tự kiểm tra (tức là phạm vi kiểm tra gián tiếp).

Đối với tôi có vẻ đủ rõ ràng rằng trong phần đầu của mã hóa, hầu hết các phương thức sẽ là các hàm cấp cao hơn bởi vì chúng được gói gọn / mô tả thiết kế.

Do đó, các phương pháp này sẽ được công khai và thử nghiệm chúng sẽ đủ dễ dàng.

Các phương pháp riêng tư sẽ đến sau khi mọi thứ hoạt động tốt và chúng tôi đang thực hiện lại vì mục đích dễ đọcsạch sẽ .


6

Như đã trích dẫn ở trên, "Nếu bạn không thử nghiệm các phương pháp riêng tư của mình, làm sao bạn biết chúng sẽ không bị hỏng?"

Đây là một vấn đề lớn. Một trong những điểm quan trọng của các bài kiểm tra đơn vị là để biết nơi nào, khi nào và làm thế nào một cái gì đó đã phá vỡ ASAP. Do đó giảm một lượng đáng kể nỗ lực phát triển & QA. Nếu tất cả những gì được kiểm tra là công khai, thì bạn không có phạm vi bảo hiểm trung thực và phân định nội bộ của lớp.

Tôi đã tìm thấy một trong những cách tốt nhất để làm điều này chỉ đơn giản là thêm tham chiếu thử nghiệm vào dự án và đặt các thử nghiệm trong một lớp song song với các phương thức riêng tư. Đưa vào logic xây dựng phù hợp để các thử nghiệm không được xây dựng trong dự án cuối cùng.

Sau đó, bạn có tất cả các lợi ích của việc thử nghiệm các phương pháp này và bạn có thể tìm thấy các vấn đề trong vài giây so với vài phút hoặc vài giờ.

Vì vậy, tóm lại, có, đơn vị kiểm tra phương pháp riêng tư của bạn.


2
Tôi không đồng ý. "Nếu bạn không thử nghiệm các phương pháp riêng tư của mình, làm sao bạn biết chúng sẽ không phá vỡ?" : Tôi biết điều này bởi vì nếu các phương thức riêng tư của tôi bị hỏng, thì các thử nghiệm kiểm tra các phương thức công khai của tôi dựa trên các phương thức riêng tư đó sẽ thất bại. Tôi không muốn phải thay đổi các bài kiểm tra của mình mỗi khi tôi thay đổi suy nghĩ về cách thực hiện các phương pháp công khai. Tôi cũng nghĩ rằng mối quan tâm chính của các bài kiểm tra đơn vị là không biết cụ thể dòng mã nào bị lỗi, thay vào đó, nó cho phép bạn ít nhiều tin tưởng rằng bạn đã không phá vỡ bất cứ điều gì khi thực hiện các thay đổi (đối với các phương thức riêng tư).
Tesseract

6

Bạn không nên . Nếu các phương thức riêng tư của bạn có đủ độ phức tạp phải được kiểm tra, bạn nên đặt chúng trên một lớp khác. Giữ sự gắn kết cao , một lớp học chỉ nên có một mục đích. Giao diện công cộng lớp nên là đủ.


3

Nếu bạn không thử nghiệm các phương pháp riêng tư của mình, làm sao bạn biết chúng sẽ không bị hỏng?


19
Bằng cách viết thông qua các bài kiểm tra phương pháp công cộng của bạn.
scubabbl

3
Các phương thức riêng tư đó được gọi là các phương thức công khai của lớp. Vì vậy, chỉ cần kiểm tra các phương thức công cộng gọi các phương thức riêng tư.
jop

1
Nếu các phương thức công khai của bạn hoạt động đúng thì rõ ràng các phương thức riêng mà chúng truy cập đang hoạt động đúng.
17 trên 26 tháng

Nếu các thử nghiệm của các phương thức công khai của bạn thất bại, bạn sẽ biết ngay rằng có gì đó không chính xác ở mức thấp hơn trong đối tượng / thành phần / v.v.
Cướp

3
Đó là thực sự tốt đẹp, tuy nhiên, để biết rằng nó là một chức năng nội bộ và không chỉ các chức năng bên ngoài mà đã phá vỡ (hoặc ngược lại rằng các chức năng bên trong cũng tốt và bạn có thể tập trung vào bên ngoài).
Adam Davis

2

Đó rõ ràng là phụ thuộc ngôn ngữ. Trước đây với c ++, tôi đã tuyên bố lớp thử nghiệm là lớp bạn bè. Thật không may, điều này không yêu cầu mã sản xuất của bạn để biết về lớp thử nghiệm.


5
Từ khóa bạn bè làm tôi buồn.
Cướp

Đây không phải là vấn đề nếu lớp thử nghiệm được thực hiện trong dự án khác. Điều quan trọng là mã sản xuất không tham chiếu lớp thử nghiệm.
Olumide

2

Tôi hiểu quan điểm trong đó các phương thức riêng tư được coi là chi tiết triển khai và sau đó không phải thử nghiệm. Và tôi sẽ tuân theo quy tắc này nếu chúng ta chỉ phải phát triển bên ngoài đối tượng. Nhưng chúng ta, có phải chúng ta là một số nhà phát triển bị hạn chế, những người chỉ phát triển bên ngoài các đối tượng, chỉ gọi các phương thức công khai của họ? Hay chúng ta thực sự cũng đang phát triển đối tượng đó? Vì chúng ta không bị ràng buộc để lập trình các đối tượng bên ngoài, chúng ta có thể sẽ phải gọi các phương thức riêng tư đó thành các phương thức công khai mới mà chúng ta đang phát triển. Sẽ không tuyệt vời khi biết rằng phương pháp riêng chống lại tất cả các tỷ lệ cược?

Tôi biết một số người có thể trả lời rằng nếu chúng ta đang phát triển một phương thức công khai khác vào đối tượng đó thì phương thức này nên được thử nghiệm và đó là (phương pháp riêng tư có thể tiếp tục sống mà không cần kiểm tra). Nhưng điều này cũng đúng với mọi phương thức công khai của một đối tượng: khi phát triển ứng dụng web, tất cả các phương thức công khai của một đối tượng được gọi từ các phương thức của bộ điều khiển và do đó có thể được coi là chi tiết triển khai cho các bộ điều khiển.

Vậy tại sao chúng ta thử nghiệm các đối tượng? Bởi vì điều này thực sự khó khăn, không thể nói là không thể chắc chắn rằng chúng tôi đang thử nghiệm các phương thức của bộ điều khiển với đầu vào thích hợp sẽ kích hoạt tất cả các nhánh của mã bên dưới. Nói cách khác, chúng ta càng ở trong ngăn xếp càng cao thì càng khó kiểm tra tất cả các hành vi. Và như vậy là tương tự cho các phương pháp riêng tư.

Đối với tôi, ranh giới giữa phương pháp riêng tư và công cộng là một tiêu chí tâm lý khi nói đến các bài kiểm tra. Các tiêu chí quan trọng hơn với tôi là:

  • là phương pháp được gọi nhiều lần từ những nơi khác nhau?
  • là phương pháp đủ tinh vi để yêu cầu kiểm tra?

1

Nếu tôi thấy rằng phương thức riêng là rất lớn hoặc phức tạp hoặc đủ quan trọng để yêu cầu các thử nghiệm riêng của nó, tôi chỉ cần đặt nó vào một lớp khác và đặt nó ở chế độ công khai ở đó (Đối tượng phương thức). Sau đó, tôi có thể dễ dàng kiểm tra phương thức riêng tư nhưng hiện tại công khai mà hiện đang sống trên lớp riêng của nó.


1

Tôi không bao giờ hiểu khái niệm về Bài kiểm tra đơn vị nhưng bây giờ tôi biết mục tiêu của nó là gì.

Một bài kiểm tra đơn vị không phải là một bài kiểm tra hoàn chỉnh . Vì vậy, nó không phải là sự thay thế cho QA và kiểm tra thủ công. Khái niệm về TDD trong khía cạnh này là sai vì bạn không thể kiểm tra mọi thứ, kể cả các phương thức riêng tư, nhưng cả các phương thức sử dụng tài nguyên (đặc biệt là các tài nguyên mà chúng tôi không kiểm soát). TDD dựa trên tất cả chất lượng của nó là điều mà nó không thể đạt được.

Một bài kiểm tra Đơn vị là một bài kiểm tra trục Bạn đánh dấu một số trục tùy ý và kết quả của trục sẽ giữ nguyên.


1

Công khai và riêng tư không phải là một sự phân biệt hữu ích cho những gì apis gọi từ các bài kiểm tra của bạn, cũng không phải là phương thức so với lớp. Hầu hết các đơn vị thử nghiệm có thể nhìn thấy trong một bối cảnh, nhưng ẩn trong các bối cảnh khác.

Vấn đề là bảo hiểm và chi phí. Bạn cần giảm thiểu chi phí trong khi đạt được các mục tiêu bảo hiểm của dự án (dòng, nhánh, đường dẫn, khối, phương thức, lớp, lớp tương đương, trường hợp sử dụng ... bất cứ điều gì nhóm quyết định).

Vì vậy, sử dụng các công cụ để đảm bảo bảo hiểm và thiết kế các thử nghiệm của bạn để gây ra chi phí ít nhất (ngắn hạn và dài hạn ).

Đừng làm các bài kiểm tra đắt hơn mức cần thiết. Nếu nó rẻ nhất để chỉ kiểm tra các điểm nhập cảnh công cộng làm điều đó. Nếu nó rẻ nhất để thử nghiệm các phương pháp riêng tư, hãy làm điều đó.

Khi bạn có nhiều kinh nghiệm hơn, bạn sẽ trở nên tốt hơn trong việc dự đoán khi nào nó đáng để tái cấu trúc để tránh chi phí bảo trì thử nghiệm dài hạn.


0

Nếu phương pháp đủ quan trọng / đủ phức tạp, tôi thường sẽ làm cho nó được "bảo vệ" và kiểm tra nó. Một số phương pháp sẽ được để riêng tư và được kiểm tra ngầm như là một phần của kiểm tra đơn vị cho các phương thức công khai / được bảo vệ.


1
@VisibleForTesting là một chú thích cho điều đó. Tôi sẽ không thư giãn đóng gói để thử nghiệm, thay vào đó hãy sử dụng dp4j.com
simpatico

0

Tôi thấy nhiều người đang ở trong cùng một dòng suy nghĩ: kiểm tra ở cấp độ công cộng. Nhưng đó không phải là những gì nhóm QA của chúng tôi làm? Họ kiểm tra đầu vào và đầu ra dự kiến. Nếu là nhà phát triển, chúng tôi chỉ kiểm tra các phương thức công khai thì chúng tôi chỉ đơn giản là làm lại công việc của QA và không thêm bất kỳ giá trị nào bằng "thử nghiệm đơn vị".


Xu hướng hiện nay là giảm hoặc không có nhóm QA. Các thử nghiệm đơn vị này trở thành các thử nghiệm tự động chạy mỗi khi một kỹ sư đẩy mã trên nhánh chính. Ngay cả với QA, không có cách nào họ có thể kiểm tra toàn bộ ứng dụng nhanh như kiểm tra tự động.
Patrick Desjardins

0

Câu trả lời cho "Tôi có nên thử nghiệm các phương pháp riêng tư không?" là một thời gian nào đó". Thông thường, bạn nên kiểm tra giao diện của các lớp.

  • Một trong những lý do là vì bạn không cần bảo hiểm gấp đôi cho một tính năng.
  • Một lý do khác là nếu bạn thay đổi các phương thức riêng tư, bạn sẽ phải cập nhật từng thử nghiệm cho chúng, ngay cả khi giao diện của đối tượng của bạn không thay đổi gì cả.

Đây là một ví dụ:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

Trong RefactoredThingbây giờ bạn có 5 bài kiểm tra, 2 trong số đó bạn phải cập nhật cho refactoring, nhưng chức năng của đối tượng của bạn thực sự đã không thay đổi. Vì vậy, hãy nói rằng mọi thứ phức tạp hơn thế và bạn có một số phương pháp xác định thứ tự của đầu ra, chẳng hạn như:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

Điều này không nên được chạy bởi người dùng bên ngoài, nhưng lớp đóng gói của bạn có thể nặng nề để chạy nhiều logic đó qua nó nhiều lần. Trong trường hợp này, có lẽ bạn muốn trích xuất nó thành một lớp riêng biệt, cung cấp cho lớp đó một giao diện và kiểm tra nó.

Và cuối cùng, hãy nói rằng đối tượng chính của bạn là siêu nặng, và phương thức này khá nhỏ và bạn thực sự cần phải đảm bảo rằng đầu ra là chính xác. Bạn đang nghĩ, "Tôi phải thử nghiệm phương pháp riêng tư này!". Bạn có biết rằng có thể bạn có thể làm cho đối tượng của mình nhẹ hơn bằng cách chuyển vào một số công việc nặng như một tham số khởi tạo không? Sau đó, bạn có thể vượt qua một cái gì đó nhẹ hơn và kiểm tra chống lại điều đó.


0

Không Bạn không nên thử Phương pháp riêng tư tại sao? và hơn nữa, khung mô phỏng phổ biến như Mockito không cung cấp hỗ trợ để thử nghiệm các phương thức riêng tư.


0

Một điểm chính là

Nếu chúng ta kiểm tra để đảm bảo tính chính xác của logic và một phương thức riêng mang logic, chúng ta nên kiểm tra nó. Phải không? Vậy tại sao chúng ta sẽ bỏ qua điều đó?

Viết bài kiểm tra dựa trên khả năng hiển thị của các phương pháp là ý tưởng hoàn toàn không liên quan.

Ngược lại

Mặt khác, gọi một phương thức riêng bên ngoài lớp gốc là một vấn đề chính. Và cũng có những hạn chế để giả định một phương thức riêng tư trong một số công cụ chế nhạo. (Ví dụ: Mockito )

Mặc dù có một số công cụ như Power Mock hỗ trợ điều đó, nhưng đây là một hoạt động nguy hiểm. Lý do là nó cần hack JVM để đạt được điều đó.

Một công việc xung quanh có thể được thực hiện là (Nếu bạn muốn viết các trường hợp thử nghiệm cho các phương thức riêng tư)

Khai báo các phương thức riêng tư như được bảo vệ . Nhưng nó có thể không thuận tiện cho một số tình huống.


0

Đó không chỉ là về các phương thức hoặc chức năng công cộng hoặc riêng tư, mà còn là về chi tiết triển khai. Các chức năng riêng tư chỉ là một khía cạnh của chi tiết thực hiện.

Xét nghiệm đơn vị, xét cho cùng, là một phương pháp thử nghiệm hộp trắng. Ví dụ, bất cứ ai sử dụng phân tích bảo hiểm để xác định các phần của mã đã bị bỏ qua trong thử nghiệm cho đến nay, đều đi vào chi tiết triển khai.

A) Có, bạn nên kiểm tra chi tiết thực hiện:

Hãy nghĩ về một hàm sắp xếp vì lý do hiệu năng sử dụng triển khai riêng tư của BubbleSort nếu có tối đa 10 phần tử và triển khai riêng của một cách tiếp cận sắp xếp khác (giả sử, heapsort) nếu có hơn 10 phần tử. API công khai là một hàm sắp xếp. Bộ kiểm tra của bạn, tuy nhiên, tốt hơn sử dụng kiến ​​thức rằng thực sự có hai thuật toán sắp xếp được sử dụng.

Trong ví dụ này, chắc chắn, bạn có thể thực hiện các thử nghiệm trên API công khai. Tuy nhiên, điều này sẽ yêu cầu phải có một số trường hợp thử nghiệm thực thi hàm sắp xếp với hơn 10 phần tử, sao cho thuật toán heapsort được kiểm tra đầy đủ. Sự tồn tại của các trường hợp thử nghiệm như vậy là một dấu hiệu cho thấy bộ thử nghiệm được kết nối với các chi tiết thực hiện của chức năng.

Nếu chi tiết triển khai của hàm sắp xếp thay đổi, có thể theo cách giới hạn giữa hai thuật toán sắp xếp bị thay đổi hoặc heapsort được thay thế bằng sáp nhập hoặc bất cứ điều gì: Các thử nghiệm hiện tại sẽ tiếp tục hoạt động. Tuy nhiên, giá trị của chúng vẫn còn nhiều nghi vấn và có thể chúng cần được làm lại để kiểm tra tốt hơn chức năng sắp xếp đã thay đổi. Nói cách khác, sẽ có một nỗ lực bảo trì mặc dù thực tế là các thử nghiệm đã có trên API công khai.

B) Cách kiểm tra chi tiết thực hiện

Một lý do tại sao nhiều người tranh luận một người không nên kiểm tra các chức năng riêng tư hoặc chi tiết triển khai là, rằng các chi tiết triển khai có nhiều khả năng thay đổi. Khả năng thay đổi cao hơn này ít nhất là một trong những lý do để ẩn chi tiết triển khai đằng sau các giao diện.

Bây giờ, giả sử rằng việc triển khai đằng sau giao diện chứa các phần riêng tư lớn hơn mà các thử nghiệm riêng lẻ trên giao diện bên trong có thể là một tùy chọn. Một số người tranh luận, những phần này không nên được kiểm tra khi riêng tư, chúng nên được biến thành một cái gì đó công khai. Khi công khai, kiểm tra đơn vị mã đó sẽ ổn.

Điều này thật thú vị: Mặc dù giao diện là nội bộ, nó có thể thay đổi, là một chi tiết triển khai. Lấy cùng một giao diện, làm cho nó công khai thực hiện một số phép biến đổi, cụ thể là biến nó thành một giao diện ít có khả năng thay đổi. Rõ ràng có một số lỗ hổng trong lập luận này.

Tuy nhiên, vẫn có một số sự thật đằng sau điều này: Khi kiểm tra các chi tiết triển khai, đặc biệt là sử dụng các giao diện nội bộ, người ta nên cố gắng sử dụng các giao diện có khả năng duy trì ổn định. Dù một số giao diện có khả năng ổn định hay không, tuy nhiên, không chỉ đơn giản là có thể quyết định dựa trên việc nó là công khai hay riêng tư. Trong các dự án từ thế giới tôi đã làm việc được một thời gian, các giao diện công cộng cũng thường đủ thay đổi và nhiều giao diện riêng vẫn không bị ảnh hưởng trong nhiều năm.

Tuy nhiên, đây là một quy tắc tốt để sử dụng "cửa trước" (xem http: // xunitpotypes.com/Principles%20of%20Test%20Automation.html ). Nhưng hãy nhớ rằng nó được gọi là "cửa trước" chứ không phải "chỉ cửa trước".

C) Tóm tắt

Kiểm tra cũng các chi tiết thực hiện. Thích thử nghiệm trên các giao diện ổn định (công khai hoặc riêng tư). Nếu chi tiết triển khai thay đổi, các thử nghiệm về API công cộng cũng cần được sửa đổi. Biến một cái gì đó riêng tư thành công cộng không thay đổi một cách kỳ diệu sự ổn định của nó.


0

Có, bạn nên thử nghiệm các phương pháp riêng tư, bất cứ nơi nào có thể. Tại sao? Để tránh sự bùng nổ không gian trạng thái không cần thiết của các trường hợp kiểm tra mà cuối cùng chỉ cần kiểm tra ngầm các chức năng riêng tư lặp đi lặp lại trên cùng một đầu vào. Hãy giải thích tại sao với một ví dụ.

Hãy xem xét ví dụ hơi khó khăn sau đây. Giả sử chúng ta muốn phơi bày công khai một hàm có 3 số nguyên và trả về true nếu và chỉ khi 3 số nguyên đó đều là số nguyên tố. Chúng tôi có thể thực hiện nó như thế này:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Bây giờ, nếu chúng ta thực hiện phương pháp nghiêm ngặt chỉ kiểm tra các chức năng công cộng, chúng ta chỉ được phép kiểm tra allPrimevà không isPrimehoặc andAll.

Là một thử nghiệm, chúng tôi có thể quan tâm Năm khả năng cho mỗi đối số: < 0, = 0, = 1, prime > 1, not prime > 1. Nhưng để kỹ lưỡng, chúng ta cũng phải xem mọi sự kết hợp của các đối số đóng với nhau như thế nào. Vì vậy, đó là 5*5*5= 125 trường hợp thử nghiệm, chúng tôi cần kiểm tra kỹ lưỡng chức năng này, theo trực giác của chúng tôi.

Mặt khác, nếu chúng ta được phép kiểm tra các chức năng riêng tư, chúng ta có thể bao quát nhiều mặt bằng với ít trường hợp kiểm tra hơn. Chúng tôi chỉ cần 5 trường hợp thử nghiệm isPrimeđể kiểm tra ở cùng cấp độ với trực giác trước đây của chúng tôi. Và theo giả thuyết phạm vi nhỏ do Daniel Jackson đề xuất, chúng tôi chỉ cần kiểm tra andAllchức năng có độ dài nhỏ, ví dụ 3 hoặc 4. Sẽ có nhiều nhất 16 bài kiểm tra nữa. Vì vậy, tổng cộng 21 bài kiểm tra. Thay vì 125. Tất nhiên, có lẽ chúng tôi sẽ muốn chạy một vài thử nghiệm trênallPrime , nhưng chúng tôi sẽ không cảm thấy bắt buộc phải bao quát toàn bộ 125 kết hợp kịch bản đầu vào mà chúng tôi nói chúng tôi quan tâm. Chỉ cần một vài con đường hạnh phúc.

Một ví dụ giả định, chắc chắn, nhưng nó là cần thiết cho một cuộc biểu tình rõ ràng. Và mô hình mở rộng cho phần mềm thực sự. Các hàm riêng thường là các khối xây dựng mức thấp nhất và do đó thường được kết hợp với nhau để mang lại logic mức cao hơn. Có nghĩa là ở cấp độ cao hơn, chúng tôi có nhiều sự lặp lại của các công cụ cấp thấp hơn do sự kết hợp khác nhau.


Trước tiên, bạn không phải kiểm tra các kết hợp như thế với các hàm thuần túy như bạn đã hiển thị. Các cuộc gọi để isPrimethực sự độc lập, do đó, kiểm tra mọi sự kết hợp một cách mù quáng là khá vô mục đích. Thứ hai, đánh dấu một chức năng thuần túy được gọi là isPrimeriêng tư vi phạm rất nhiều quy tắc thiết kế mà tôi thậm chí không biết bắt đầu từ đâu. isPrimerất rõ ràng là một chức năng công cộng. Điều đó đang được nói, tôi có được những gì bạn đang nói bất kể ví dụ cực kỳ nghèo nàn này. Tuy nhiên, nó được xây dựng dựa trên tiền đề mà bạn muốn thực hiện kiểm tra kết hợp, khi trong các hệ thống phần mềm thực sự, điều này hiếm khi là một ý tưởng tốt.
Matt Messersmith

Matt có ví dụ không lý tưởng tôi sẽ cung cấp cho bạn điều đó. Nhưng nguyên tắc nên rõ ràng.
Colm Bhandal

Tiền đề không chính xác là bạn muốn thực hiện kiểm tra kết hợp. Đó là điều bạn phải làm, nếu bạn hạn chế chỉ thử nghiệm những mảnh ghép công khai. Có những trường hợp bạn muốn đặt một chức năng thuần túy riêng tư để tuân thủ các nguyên tắc đóng gói thích hợp. Và chức năng riêng tư thuần túy này có thể được sử dụng bởi những người công cộng. Trong một cách kết hợp, với các chức năng riêng tư thuần túy khác có lẽ. Trong trường hợp đó, theo giáo điều rằng bạn sẽ không kiểm tra riêng tư, bạn sẽ bị buộc phải thực hiện kiểm tra kết hợp trên chức năng công cộng thay vì kiểm tra mô-đun các thành phần riêng tư.
Colm Bhandal

0

Bạn cũng có thể đặt phương thức của mình ở chế độ riêng tư tức là mặc định và bạn sẽ có thể đơn vị kiểm tra nó trừ khi nó được yêu cầu là riêng tư.

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.