2013-01-17 36 views
20

Tôi hỏi vì tôi đang cố gắng sử dụng một khung mocking (Mockito) mà không cho phép bạn giả lập các phương thức tĩnh. Nhìn vào nó tôi đã tìm thấy khá một vài bài viết trên blog nói rằng bạn nên có càng ít phương pháp tĩnh càng tốt, nhưng tôi đang gặp khó khăn trong việc quấn quanh đầu tôi. Cụ thể là tại sao các phương thức không sửa đổi trạng thái toàn cầu và về cơ bản là các phương thức trợ giúp. Ví dụ tôi có một lớp gọi là ApiCaller có một số phương pháp tĩnh. Một trong những mục đích của phương thức tĩnh là thực hiện một cuộc gọi HTTP, xử lý mọi vấn đề tùy chỉnh mà máy chủ của chúng tôi có thể đã trả lại (ví dụ: người dùng không đăng nhập) và trả về phản hồi. Để đơn giản hóa, một cái gì đó như:Tại sao lại sử dụng các phương thức trợ giúp tĩnh trong Java?

public class ApiCaller { 
... 
    public static String makeHttpCall(Url url) { 
     // Performs logic to retrieve response and deal with custom server errors 
     ... 
     return response; 
    } 
} 

Để sử dụng tất cả điều này tôi phải làm là gọi ApiCaller.makeHttpCall(url) Bây giờ tôi có thể dễ dàng thực hiện điều này một phương pháp không tĩnh như:

public class ApiCaller { 
... 
    public String makeHttpCall(Url url) { 
     // Performs logic to retrieve response and deal with custom server errors 
     ... 
     return response; 
    } 
} 

và sau đó sử dụng phương pháp này hãy gọi new ApiCaller().makeHttpCall() nhưng điều này có vẻ như là phí phụ trội. Bất cứ ai có thể giải thích tại sao điều này là xấu và nếu có một giải pháp tốt hơn để làm cho các phương pháp không tĩnh (khác hơn là chỉ cần loại bỏ các từ khóa) để tôi có thể stub ra các phương pháp này bằng cách sử dụng khuôn khổ mocking?

Cảm ơn!

+2

Hoàn toàn không có gì sai với một phương pháp tĩnh, nếu nó là một hàm "thuần túy", hoặc nếu nó chỉ tham chiếu đến "hệ thống" các công cụ sao cho nó không có trạng thái cục bộ. Nếu nó tham chiếu globals bạn cần phải tread một chút cẩn thận hơn, mặc dù. –

+1

@HotLicks Một số ví dụ về "công cụ hệ thống" là gì? Ngoài ra, một số ví dụ về tình trạng địa phương là gì? Hệ thống tệp có được tính là trạng thái cục bộ không? Có một trang web không? –

+1

"Công cụ hệ thống" có thể là, ví dụ: truy xuất thống kê lưu trữ quy trình hoặc tìm nạp thời gian trong ngày hoặc một số thông tin như vậy. "Trạng thái cục bộ" ngụ ý rằng phương thức này đang stashing dữ liệu trong một 'static' hoặc một số thứ như vậy - nó thực sự là một đối tượng giả vờ là không. –

Trả lời

9

Vấn đề với phương pháp tĩnh là chúng rất khó giả mạo khi chúng không liên quan đến hệ thống bạn đang thử nghiệm. Hãy tưởng tượng mã này:

public void systemUnderTest() { 
    Log.connectToDatabaseForAuditing(); 
    doLogicYouWantToTest(); 
} 

Phương pháp connectToDatabaseForAuditing() là tĩnh. Bạn không quan tâm phương pháp này làm gì cho bài kiểm tra bạn muốn viết. Tuy nhiên, để kiểm tra mã này ngay bây giờ bạn cần một cơ sở dữ liệu có sẵn.

Nếu không tĩnh mã sẽ trông như thế này:

private Logger log; //instantiate in a setter AKA dependency injection/inversion of control 

public void systemUnderTest() { 
    log.connectToDatabaseForAuditing(); 
    doLogicYouWantToTest(); 
} 

Và thử nghiệm của bạn sẽ là tầm thường để viết mà không có một cơ sở dữ liệu bây giờ:

@Before 
public void setUp() { 
    YourClass yourClass = new YourClass(); 
    yourClass.setLog(new NoOpLogger()); 

} 

//.. your tests 

Hãy tưởng tượng đang cố gắng để làm điều đó khi phương pháp là tĩnh. Tôi thực sự không thể nghĩ ra một cách nào ngoại trừ việc sửa đổi logger để có một biến tĩnh gọi là inTestMode mà bạn đặt thành true trong setUp() để đảm bảo nó không kết nối với cơ sở dữ liệu.

+2

Tôi hiểu từ quan điểm nhạo báng lý do tại sao nó không cần phải có phương pháp tĩnh, nhưng tôi ít nhiều cố gắng tìm ra lý do tại sao nhiều người đang sử dụng phương pháp tĩnh là thực hành lập trình kém (ví dụ: http: // blog. msdn.com/b/nickmalik/archive/2005/09/06/461404.aspx). Có lẽ mặc dù đây là một lý do đủ tốt, tất cả các mã nên được testable .. – odiggity

+1

Tôi nghĩ rằng tôi đang ở trong cùng một thuyền như bạn. Vấn đề tôi có với nguyên tắc SOLID là họ giải thích * cái gì * nhưng không * tại sao *.Một điều tôi thấy thú vị là bạn có ** vấn đề chính xác ** và ** giải pháp chính xác ** mà tôi đã mô tả ở trên nếu bạn hỏi câu hỏi này về mẫu Singleton. Có lẽ đó là nhiều hơn một sự trùng hợp ngẫu nhiên. –

+0

Trong thực tế, cho dù nó là dễ dàng, khó khăn, hoặc không thể giả hoặc giả một phương pháp tĩnh phụ thuộc hoàn toàn vào công cụ chế nhạo bạn đang sử dụng. Với Mockito, điều đó là không thể. Với Mockito + PowerMock nó có thể, mặc dù không chính xác dễ dàng (do các API Mockito và PowerMock là hai API riêng biệt, và cũng do các yêu cầu nhất định để sử dụng API PowerMock). Với JMockit (công cụ của riêng tôi), thật dễ dàng: chỉ cần khai báo trường '/Mocked ApiCaller mock'/tham số và sau đó ghi lại hoặc kiểm tra các kỳ vọng về bất kỳ phương thức tĩnh nào trong' ApiCaller' (mặc dù nó có thể có thiết kế tốt hơn trường hợp). –

2

Nó ít mô đun hơn. Thay vào đó, bạn nên định nghĩa một giao diện ApiCaller với phương thức thể hiện makeHttpCall() để bạn có thể xác định các triển khai riêng biệt trong tương lai.

Ít nhất bạn sẽ luôn có 2 triển khai giao diện, phiên bản gốc và phiên bản được chế nhạo.

(Lưu ý: có một số khuôn khổ mocking cho phép bạn thử phương pháp tĩnh)

Là một phụ lục, trong khi điều này có thể không phải là trường hợp trong ứng dụng cụ thể của bạn, thường sử dụng các phương pháp tĩnh là dấu hiệu của một giám sát thiết kế lớn hơn. Thiết kế cho mô đun và khả năng tái sử dụng nên phổ biến trong suốt ứng dụng của bạn, bởi vì ngay cả khi bạn không cần nó ngay bây giờ bạn có thể cần trong tương lai và khó hơn nhiều và mất nhiều thời gian hơn để thay đổi mọi thứ sau khi thực tế .

+0

Mặc dù đó là một ý tưởng hay, nhưng bạn không phải làm điều đó khi sử dụng Mockito. Bạn có thể giả lập lớp bê tông và giả mạo chức năng phương thức cụ thể. –

+0

Tôi đã nghe cuộc tranh luận này trước đây nhưng tôi không bị thuyết phục. Tôi có thể xem nó sẽ áp dụng như thế nào trong một số trường hợp. Ví dụ một lớp lưu trữ có các chức năng chung như lưu trữ, truy xuất, cập nhật và việc triển khai thực tế của nó có thể thực sự được hoán đổi với một thứ khác. Tuy nhiên, trong trường hợp của tôi, ApiCaller là một lớp rất độc đáo và không có lý do hợp lý nào tôi có thể nghĩ tại sao nó lại bị 'hoán đổi' để thực hiện một giao diện khác. Một số chức năng của logic có thể được thay đổi nhưng không phải toàn bộ điều và tạo ra một giao diện có vẻ như quá mức cần thiết. – odiggity

+0

@DanielKaplan Vâng, đó là những gì tôi đã đề cập đến khi tôi nói phiên bản chế nhạo. Bất kể bạn viết tay nó hay khung công tác tạo nó cho bạn, nó vẫn là một * triển khai * của giao diện của bạn –

0

Bạn không thể giả lập chúng dễ dàng khi bạn cần trả lời khá nhiều câu hỏi của riêng bạn.

Đặc biệt khi có điều gì đó như được hiển thị: thực hiện cuộc gọi HTTP tốn kém và thực hiện việc đó cho đơn vị kiểm tra mã của bạn không có ý nghĩa – lưu mã đó để thử nghiệm tích hợp.

Kiểm tra đơn vị yêu cầu câu trả lời đã biết (và mã phản hồi) từ cuộc gọi HTTP, điều mà bạn không thể thực hiện nếu bạn gọi cho ai đó dịch vụ của người khác bằng cách sử dụng mạng bạn không kiểm soát.

1

TƯ NHÂN Các phương thức trợ giúp tĩnh không phải là xấu, trên thực tế, chúng thực sự được ưu tiên tại công ty lớn nơi tôi làm việc. Và tôi sử dụng Mockito với họ mọi lúc, truy cập từ các phương thức gọi phương thức trợ giúp tĩnh.

Có một sự khác biệt nhỏ trong cách trình biên dịch xử lý một phương thức trợ giúp tĩnh. Mã byte được tạo sẽ dẫn đến hướng dẫn invokestatic và nếu bạn xóa kết quả tĩnh sẽ là một trong các hướng dẫn khác, chẳng hạn như invokespecial. Sự khác biệt có được rằng invokestatic tải lớp để truy cập các phương pháp, nơi invokespecial pops đối tượng ra khỏi ngăn xếp đầu tiên. Vì vậy, có thể có một lợi thế hiệu suất nhỏ (có thể không).

Các vấn đề liên quan