Tại sao Mockito không giả lập các phương thức tĩnh?


267

Tôi đã đọc một vài chủ đề ở đây về các phương thức tĩnh và tôi nghĩ rằng tôi hiểu các vấn đề lạm dụng / sử dụng quá mức các phương thức tĩnh có thể gây ra. Nhưng tôi đã không thực sự đi đến tận cùng lý do tại sao thật khó để chế nhạo các phương thức tĩnh.

Tôi biết các khung mô phỏng khác, như PowerMock, có thể làm điều đó nhưng tại sao Mockito không thể?

Tôi đọc bài viết này , nhưng tác giả dường như tôn giáo chống lại từ này static, có thể đó là sự hiểu biết kém của tôi.

Một lời giải thích / liên kết dễ dàng sẽ là tuyệt vời.


12
Chỉ là một lưu ý phụ: PowerMock không phải là một thư viện đối tượng giả, nó chỉ thêm các tính năng đó (chế độ chế tạo và chế độ giả) trên đầu các thư viện khác. Chúng tôi sử dụng PowerMock + Mockito trong công việc, chúng nổi tốt với nhau.
Matthias

Câu trả lời:


238

Tôi nghĩ lý do có thể là các thư viện đối tượng giả thường tạo ra các giả bằng cách tạo động các lớp khi chạy (sử dụng cglib ). Điều này có nghĩa là họ hoặc thực hiện một giao diện trong thời gian chạy (đó là những gì EasyMock làm nếu tôi không nhầm) hoặc họ thừa hưởng từ lớp để chế giễu (đó là những gì Mockito làm nếu tôi không nhầm). Cả hai cách tiếp cận đều không hiệu quả đối với các thành viên tĩnh, vì bạn không thể ghi đè chúng bằng cách sử dụng tính kế thừa.

Cách duy nhất để giả định thống kê là sửa đổi mã byte của lớp trong thời gian chạy, mà tôi cho rằng có liên quan nhiều hơn một chút so với kế thừa.

Đó là suy đoán của tôi về nó, vì những gì nó đáng giá ...


7
Điều này cũng đúng với việc chế giễu các nhà xây dựng. Những người cũng vậy, không thể thay đổi thông qua thừa kế.
Matthias

11
Cũng có thể đáng bổ sung rằng một số người đề xuất TDD / TBD nhận thấy việc thiếu phương pháp tĩnh và chế tạo chế tạo là một điều tốt . Họ lập luận rằng khi bạn thấy mình phải chế giễu các phương thức tĩnh hoặc các hàm tạo, thì đây là một chỉ báo cho thiết kế lớp kém. Chẳng hạn, khi tuân theo cách tiếp cận IoC thuần túy trong việc lắp ráp các mô-đun mã của bạn, bạn thậm chí sẽ không bao giờ có nhu cầu chế nhạo các thống kê hoặc công cụ kiểm tra (trừ khi chúng là một phần của một số thành phần hộp đen). Xem thêm giorgiosironi.blogspot.com/2009/11/ Mạnh
Matthias

200
Tôi nghĩ rằng các công cụ chế giễu sẽ cung cấp cho bạn những gì bạn cần mà không cho rằng họ biết những gì tốt hơn cho bạn. Ví dụ, nếu tôi đang sử dụng thư viện của bên thứ ba sử dụng lệnh gọi phương thức tĩnh mà tôi cần để giả, thì thật tuyệt khi có thể làm như vậy. Ý tưởng rằng một khung giả sẽ không cung cấp cho bạn một số khả năng bởi vì nó được xem là thiết kế xấu về cơ bản là thiếu sót.
Lo-Tan

11
@ Lo-Tan - điều đó giống như nói rằng một ngôn ngữ nên có khả năng về mọi thứ, không cho rằng nó biết rõ hơn bạn. Đó chỉ là sự phù phiếm từ phía bạn, bởi vì chúng trở nên hùng vĩ. Vấn đề ở đây là cuộc chiến "chống / chống tĩnh" không rõ ràng, và các khuôn khổ cũng vậy. Tôi đồng ý rằng chúng ta nên có cả hai. Nhưng nơi mà sự thật rõ ràng, tôi thích một khuôn khổ áp đặt những sự thật đó. Đó là một cách học - công cụ giúp bạn đi đúng hướng. Vì vậy, bản thân bạn không phải. Nhưng bây giờ mỗi đầu mì có thể áp đặt cái gọi là "thiết kế tốt" của họ. "
Thiếu

13
@nevvermind ơi? Một ngôn ngữ cấp cao có nghĩa là để giúp bạn và có sự trừu tượng cần thiết để bạn có thể tập trung vào các phần quan trọng. Thư viện thử nghiệm là một công cụ - một công cụ tôi sử dụng để tạo ra chất lượng tốt hơn và hy vọng mã được thiết kế tốt hơn. Điểm của thư viện thử nghiệm / giả là gì khi nó có những hạn chế có nghĩa là tôi không thể sử dụng nó khi tôi phải tích hợp mã được thiết kế kém của người khác? Có vẻ như không nghĩ ra, trong khi đó, ngôn ngữ tốt đã được .
Lo-Tan

28

Nếu bạn cần chế nhạo một phương thức tĩnh, đó là một chỉ báo mạnh cho một thiết kế xấu. Thông thường, bạn chế giễu sự phụ thuộc của bài kiểm tra dưới lớp của bạn. Nếu bài kiểm tra dưới lớp của bạn đề cập đến một phương thức tĩnh - ví dụ như java.util.Math # sin - điều đó có nghĩa là bài kiểm tra dưới lớp cần chính xác việc triển khai này (ví dụ về độ chính xác so với tốc độ). Nếu bạn muốn trừu tượng hóa từ một triển khai xoang cụ thể, có lẽ bạn cần một Giao diện (bạn thấy nơi này sẽ diễn ra)?


3
Chà, tôi đã sử dụng các phương thức tĩnh để cung cấp các bản tóm tắt cấp cao, chẳng hạn như "mặt tiền tồn tại tĩnh". Mặt tiền như vậy giữ cho mã máy khách tránh xa sự phức tạp và chi tiết cấp thấp của API ORM, cung cấp API phù hợp và dễ sử dụng hơn, đồng thời cho phép rất linh hoạt.
Rogério

Và tại sao bạn phải chế nhạo nó? Nếu bạn phụ thuộc vào phương thức tĩnh, "đơn vị" hoặc "mô-đun" không chỉ là lớp mà còn bao gồm "mặt tiền kiên trì tĩnh".
Jan

88
Đúng, nhưng đôi khi bạn có thể không có lựa chọn nếu chẳng hạn, bạn cần phải giả định một phương thức tĩnh trong một số lớp bên thứ ba.
Stijn Geukens

6
Đúng, nhưng đôi khi chúng ta có thể giao dịch với những người độc thân.
Manu Manjunath

Suy nghĩ duy nhất không thể giải quyết bằng cách trừu tượng hóa là quá nhiều mức độ trừu tượng ... Thêm các lớp trừu tượng làm tăng thêm sự phức tạp và thường không cần thiết. Tôi nghĩ về các ví dụ mà tôi đã thấy về các khung cố gắng giả định System.civerseTimeMillis () bằng cách gói cuộc gọi đơn giản này vào một lớp duy nhất. Chúng tôi kết thúc với một lớp singleton cho mỗi phương thức thay vì chỉ có các phương thức - chỉ để tạo điều kiện kiểm tra. Và sau đó khi bạn giới thiệu một dep bên thứ 3 gọi phương thức tĩnh trực tiếp thay vì thông qua trình bao bọc đơn lẻ của bạn, các thử nghiệm vẫn thất bại ...
Cha Jeremy Krieg

5

Tôi thực sự nghĩ rằng đó là mùi mã nếu bạn cũng cần phải chế giễu các phương thức tĩnh.

  • Phương pháp tĩnh để truy cập chức năng chung? -> Sử dụng một cá thể đơn lẻ và tiêm
  • Mã bên thứ ba? -> Gói nó vào giao diện / đại biểu của riêng bạn (và nếu cần, hãy biến nó thành một singleton)

Lần duy nhất điều này có vẻ quá mức đối với tôi, là những người như Guava, nhưng bạn không cần phải chế giễu kiểu này vì dù sao nó cũng là một phần của logic ... (những thứ như Iterables.transform (..))
Đó là mã của riêng bạn giữ sạch sẽ, bạn có thể chế nhạo tất cả các phụ thuộc của bạn một cách sạch sẽ, và bạn có một lớp chống tham nhũng chống lại các phụ thuộc bên ngoài. Tôi đã thấy PowerMock trong thực tế và tất cả các lớp chúng tôi cần nó đều được thiết kế kém. Ngoài ra, việc tích hợp PowerMock đôi khi gây ra sự cố nghiêm trọng
(ví dụ: https://code.google.com.vn/p/powermock/issues/detail?id=355 )

PS: Tương tự giữ cho các phương thức riêng tư, quá. Tôi không nghĩ các bài kiểm tra nên biết về các chi tiết của các phương pháp riêng tư. Nếu một lớp phức tạp đến mức nó cám dỗ để chế nhạo các phương thức riêng tư, thì đó có lẽ là một dấu hiệu để tách lớp đó ...


2
Singleton sẽ khiến bạn gặp phải tất cả các vấn đề, đặc biệt là khi bạn nhận ra rằng bạn thực sự cần nhiều hơn một trường hợp và bây giờ bạn cần cấu trúc lại toàn bộ hệ thống của mình để thực hiện điều đó.
Ricardo Freitas

Tôi không nói, rằng tôi giới thiệu Mẫu Singleton cho mọi người. Ý tôi là, nếu tôi phải quyết định giữa một lớp tiện ích tĩnh và Singleton cung cấp cùng chức năng, tôi sẽ chọn Singleton. Và nếu một lớp có phải là Singleton hay không thì nên được kiểm soát bởi khung DI, trong lớp I @Inject SomeDependencyvà trong cấu hình của tôi, tôi xác định bind(SomeDependency.class).in(Singleton.class). Vì vậy, nếu ngày mai nó không còn là Singleton nữa, tôi thay đổi một cấu hình và đó là nó.
pete83

@ pete83 Em nghe anh. Tuy nhiên, tôi có một vấn đề với các lib hoặc khung kiểm tra yêu cầu các nhà phát triển thay đổi thiết kế của họ để đáp ứng thiết kế / giới hạn của khung kiểm tra. Đó là IMO đặt xe ngựa trước ngựa, hoặc đuôi vẫy chó.
Matt Campbell

1
Lập luận đó làm cho rất ít ý nghĩa với tôi. Các mẫu Singleton đã không còn được ưa chuộng trong nhiều năm nay, vì quá nhiều lý do để liệt kê ở đây. Những gì cấu thành mã "sạch"? Nếu tôi có một phương thức cá thể lớp gọi một phương thức trợ giúp tĩnh trả về một số thao tác I / O, tại sao tôi không muốn điều đó bị chế giễu trong một bài kiểm tra? Và làm thế nào là thiết kế kém? Tất cả các phương pháp vắt tay xung quanh phương pháp tĩnh chế nhạo này không thêm vào. Mocking một phương pháp là ngược lại với thử nghiệm nó. Nếu quá khó để thực hiện thì chỉ cần nói và thực hiện với nó
eggmatters

Ôi trời, tôi chưa bao giờ nói về mẫu Singleton cũ mà mọi người gọi Foo.getInstance()ở khắp mọi nơi. Tôi chỉ viết singleton trong câu trả lời để chống lại đối số "nhưng một phương thức tĩnh không yêu cầu tạo ra nhiều đối tượng trình bao bọc". Ngoài ra, về mặt khái niệm đối với tôi có rất ít sự khác biệt giữa một phương thức tĩnh và một phương thức cá thể trên một singleton, chỉ là bạn không thể chế giễu cộng tác viên đơn lẻ này. Nhưng singleton hay không hoàn toàn không phải là điểm mà tôi đang cố gắng thực hiện, vấn đề là tiêm và chế nhạo cộng tác viên và không gọi các phương thức tĩnh nếu nó làm cho việc kiểm tra khó khăn.
pete83

4

Mockito trả về các đối tượng nhưng tĩnh có nghĩa là "cấp độ lớp, không phải cấp độ đối tượng" Vì vậy, mockito sẽ đưa ra ngoại lệ con trỏ null cho tĩnh.


0

Trong một số trường hợp, các phương thức tĩnh có thể khó kiểm tra, đặc biệt nếu chúng cần được chế giễu, đó là lý do tại sao hầu hết các khung mô phỏng không hỗ trợ chúng. Tôi thấy bài viết trên blog này rất hữu ích trong việc xác định cách mô phỏng các phương thức và lớp tĩnh.


1
Việc mô phỏng các phương thức tĩnh thậm chí còn dễ dàng hơn so với việc chế tạo các phương thức cá thể (vì không có phiên bản nào), khi sử dụng API chế biến phù hợp.
Rogério

Điều này giống như trả lời câu hỏi bằng chính câu hỏi, đó là lý do tại sao khó thực hiện điều này, mà đây không phải là một câu trả lời.
Matthias

40
Tôi đã đánh giá thấp nó bởi vì bài đăng trên blog đề xuất một cách giải quyết tốn kém (tái cấu trúc mã sản xuất), thay vì thực sự giải quyết vấn đề cách ly một lớp khỏi các phương thức tĩnh mà nó xảy ra để sử dụng. IMO, một công cụ chế giễu thực sự thực hiện công việc sẽ không phân biệt đối xử với bất kỳ phương pháp nào; một nhà phát triển nên được tự do quyết định việc sử dụng các phương thức tĩnh là tốt hay xấu trong một tình huống nhất định, thay vì bị ép xuống một con đường.
Rogério
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.