2012-02-02 28 views
18

Hãy tưởng tượng sau mã:Mockito - gián điệp trên các đối tượng thực gọi phương thức gốc

List list = ..... 
List spy = spy(list); 
doThrow(new NullpointerException()).when(spy).get(0); 

doThrow(....) thực hiện list.get(0) - điều này làm cho không có ý nghĩa gì cả. Tôi muốn xác định hành vi giả lập và không gọi một phương pháp ở đây ..... Tôi có thiếu cái gì?

EDIT: Danh sách được trang trí bởi CGLIB. Khi tôi loại bỏ CGLIB proxy Mockito hoạt động như mong đợi. Bất kỳ ý tưởng làm thế nào để giải quyết vấn đề như vậy khi sử dụng proxy CGLIB?

Cảm ơn, Maciej

+0

Có vẻ như đây thực sự chỉ là cú pháp của mockito, sử dụng trình tạo mẫu. (giống như rất nhiều mocking-frameworks khác) –

+0

Theo như tôi biết Mockito, mã của bạn là chính xác. Tài liệu cho biết 'doThrow' chỉ dành cho các phương thức void, vì vậy bạn có thể thử lại với ví dụ: 'clear()' để xem đây có phải là vấn đề không. Nhưng tôi không thể tưởng tượng tại sao 'doThrow' không hoạt động trên các phương thức không trống như' doReturn'. – flyx

+0

Không chắc chắn những gì bạn đang yêu cầu ở đây. Có, bạn có thể sử dụng doThrow(), ngay cả đối với các phương thức không có giá trị. Lý do tại sao các tài liệu đề cập đến phương pháp void là bởi vì có một cú pháp thứ hai cho ném stubbing, mà không làm việc cho các phương pháp void, và được trình bày trước khi doThrow một trong các tài liệu. Nhận được (0) trong ví dụ của bạn chỉ được gọi là gián điệp, không phải là Danh sách thực. Các gián điệp biết rằng đó là trong bối cảnh của một doThrow, vì vậy thay vì gọi REAL nhận được (0), nó stubs nó thay thế. Đó là những gì bạn đang yêu cầu? Nếu vậy, tôi sẽ biến nhận xét này thành câu trả lời. –

Trả lời

6
import static org.mockito.Mockito.doThrow; 
import static org.mockito.Mockito.spy; 

import java.lang.reflect.Method; 

import org.junit.Test; 

import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 

public class MockitoSpyTest { 

    @Test 
    public void execTest() { 

     System.out.println("*** TEST 1 ***"); 
     System.out.println("Test on unmodified object"); 
     MySet ms = new MySetImpl(); 
     ms.set("test value"); 
     System.out.println("Set contains: " + ms.get()); 

     // decorate ms1 with easymock 
     System.out.println("\n*** TEST 2 ***"); 
     MySet spyMs = spy(ms); 
     doThrow(new NullPointerException("my test nullpointer")).when(spyMs).get(); 
     System.out.println("Test decorated object with SPY"); 
     spyMs.set("test value"); 
     try { 
      System.out.println("Set contains: " + spyMs.get()); 
     } catch (NullPointerException e) { 
      System.out.println("NullPointerException - as expected"); 
     } 

     // Enhance call with CGLIB 
     System.out.println("\n*** TEST 3 ***"); 
     System.out.println("Test on CGLIB decorated object"); 
     Enhancer enc = new Enhancer(); 
     enc.setSuperclass(MySetImpl.class); 
     enc.setInterfaces(new Class[] { MySet.class }); 
     enc.setCallback(new MethodInterceptor() { 

      @Override 
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
       if ("get".equals(method.getName())) { 
        System.out.println("CGLIB decorated GET call"); 
       } 
       return proxy.invokeSuper(obj, args); 
      } 
     }); 
     MySet ms1 = (MySet) enc.create(); 
     ms1.set("test value"); 
     System.out.println("Set contains: " + ms1.get()); 

     // decorate ms1 with easymock 
     System.out.println("\n*** TEST 4 ***"); 
     System.out.println("Test on CGLIB decorated object with SPY"); 
     MySet spyMs1 = spy(ms1); 
     doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get(); 
     spyMs1.set("test value"); 
     System.out.println("Set contains: " + spyMs1.get()); 
    } 

    public interface MySet { 
     void set(String val); 

     String get(); 
    } 

    public static class MySetImpl implements MySet { 
     String val; 

     public void set(String val) { 
      this.val = val; 
      System.out.println("Original SET call:" + val); 
     } 

     public String get() { 

      System.out.println("Original GET call:" + val); 
      return val; 
     } 

    } 
} 

Ví dụ trên sản lượng sản xuất:

*** TEST 1 *** 
Test on unmodified object 
Original SET call:test value 
Original GET call:test value 
Set contains: test value 

*** TEST 2 *** 
Test decorated object with SPY 
Original SET call:test value 
NullPointerException - as expected 

*** TEST 3 *** 
Test on CGLIB decorated object 
Original SET call:test value 
CGLIB decorated GET call 
Original GET call:test value 
Set contains: test value 

*** TEST 4 *** 
Test on CGLIB decorated object with SPY 
CGLIB decorated GET call 
Original GET call:test value 
Original SET call:test value 
CGLIB decorated GET call 
Original GET call:test value 
Set contains: test value 

Bây giờ TEST 2TEST 4 nên ném NullPointerException trên get cuộc gọi - dựa trên gián điệp Mockito: doThrow(new NullPointerException("my test nullpointer")).when(spyMs1).get();

Các "TEST 4" không ném dự kiến ngoại lệ bởi vì nó đã được trang trí với CGLIB - chúng ta cũng có thể thấy trên bàn điều khiển gọi CGLIb đang được thực hiện: GLIB decorated GET call và không gọi vào đối tượng gián điệp. Hiệu ứng tương tự có thể đạt được khi sử dụng Spring AOP với các proxy CGLIB.

+0

Ví dụ tuyệt vời. Tuy nhiên, tôi không thể thấy điều này đã trở thành câu trả lời như thế nào. Tôi nhận được cùng một vấn đề khi tôi đang sử dụng Spring AOP với Mockito. Tôi không thể theo dõi các phương pháp mà tôi đang theo dõi. – supertonsky

+0

supertonsky, tôi đoán câu trả lời, tóm lại, là người ta không thể tạo ra điệp viên Mockito trên các đối tượng tăng cường CgLib. :( – bcody

0
Mockito.doThrow(new NullpointerException()).when(spy).get(0); 

Tôi nghĩ vấn đề ở đây là bạn đang cố gắng để làm một mô hình một phần và do đó bạn cần phải có chú thích trên lớp thử nghiệm của bạn:

@PrepareForTest(List.class) 

Điều này có thể có hoặc không hoạt động. Nhìn vào mã của tôi, nơi tôi kiểm tra xử lý ngoại lệ, tôi luôn luôn thực hiện nó trên một đối tượng được chế tạo hoàn toàn, không phải là một đối tượng được mô phỏng một phần. Ngoài ra, tôi đã sử dụng rộng rãi PowerMockito khi chế nhạo từng phần, vì vậy có thể thư viện sẽ làm những gì bạn cần.

+0

Bạn đang gây nhầm lẫn. ** A.** Mockito có thể thực hiện một phần mocks trong một thời gian qua [Mockito # spy()] (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#spy (T)) . Tuy nhiên Mockito không giả lập các lớp cuối cùng hoặc các phương thức cuối cùng, đó là nơi PowerMock có thể trở nên hữu ích. ** B. ** Bạn đang đề cập đến '@PrepareForTest (List.class)' không liên quan và sai vì 2 lý do: 1. Danh sách là một giao diện nên nó sẽ không gây ra vấn đề gì với giả lập, 't làm việc cho các lớp hệ thống (mà không cần thêm một tác nhân). – Brice

+0

Điều này có thể hoạt động, bởi vì điện giả tạo ra một proxy khác. Tôi đã thử nó, và tôi đang nhận được ngoại lệ javassis - nó seams rằng trong trường hợp này chúng tôi có xung đột giữa cglib và javassis –

+4

Bạn những gì không phải là tốt đẹp? Downvoting người tháng sau đó. Nếu API thay đổi sau một năm, tại sao lại phạt tôi? –

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