2015-04-29 12 views
14

Tôi đã rất ngạc nhiên khi phát hiện ra rằng sau mã ví dụ đơn giản không làm việc cho tất cả các phiên bản Mockito> 1.8.5Mockito @InjectMocks không hoạt động cho các trường có cùng loại

@RunWith(MockitoJUnitRunner.class) 
public class MockitoTest { 

    @Mock(name = "b2") 
    private B b2; 

    @InjectMocks 
    private A a; 

    @Test 
    public void testInjection() throws Exception { 
     assertNotNull(a.b2); //fails 
     assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here 
    } 

    static class A{ 
     private B b1; 
     private B b2; 
    } 

    interface B{} 
} 

Trong javadocs (http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) có một quote:

Lưu ý 1: Nếu bạn có các lĩnh vực với cùng một loại (hoặc tương tự tẩy xoá), đó là tốt hơn để đặt tên cho tất cả @Mock lĩnh vực chú thích với các lĩnh vực phù hợp, khác Mockito có thể bị lẫn lộn và tiêm sẽ không xảy ra.

Liệu nó có nghĩa rằng nếu tôi có một số lĩnh vực với cùng một loại I không thể giả duy nhất của họ mà là cần xác định @Mock cho ALL lĩnh vực với cùng loại? Có phải giới hạn được biết đến và có bất kỳ lý do nào tại sao nó chưa được khắc phục? Cần phải đơn giản để khớp với @Mock theo tên trường, phải không?

Trả lời

17

Có vẻ Mockito sử dụng một thuật toán được mô tả trong their JavaDoc

Nếu tôi hiểu đúng, nó sẽ trước sắp xếp theo loại (trong trường hợp này chỉ có 1 B) và sau đó sắp xếp vào tên (không có thay đổi ở đây). Nó cuối cùng sẽ tiêm bằng cách sử dụng the OngoingInjector interface implementation, xuất hiện để tìm kiếm trường đầu tiên và tiêm nó.

Vì bạn chỉ có 1 B được xác định và có 2 trường B trong Mock, nó sẽ thấy kết quả của trường hợp đầu tiên đến trường và dừng. Điều này là do mocks.size() == 1 trong NameBasedCandidateFilter . Do đó nó sẽ ngừng lọc và tiêm trực tiếp. Nếu bạn tạo nhiều mocks cùng loại, chúng sẽ được sắp xếp theo Tên và được tiêm tương ứng.

Tôi có thể làm cho nó hoạt động khi tôi tạo nhiều mocks (nhưng nhỏ hơn số lượng trường) của một loại cụ thể.

@RunWith(MockitoJUnitRunner.class) 
public class MockitoTest { 

    @Mock(name = "b2") 
    private B b2; 

    @Mock(name = "b3") 
    private B b3; 

    @InjectMocks 
    private A a; 

    @Test 
    public void testInjection() { 
     System.out.println(this.a); 
    } 

    static class A { 

     private B b1; 

     private B b2; 

     private B b3; 
    } 

    interface B { 
    } 
} 

này sẽ bơm một cách chính xác b2 vào a.b2 và b3 vào a.b3 thay vì a.b1 và a.b2 (2 lĩnh vực đầu tiên được quy định tại A).

Bạn luôn có thể để lại vấn đề GitHub trên kho lưu trữ của họ bằng cách tăng cường hoặc thay đổi thuật toán lọc phun để được xem xét.

+1

Thực tế tiêm chắc chắn có thể được thực hiện thông minh hơn một chút cho các trường hợp hợp pháp như vậy, nhưng sự an toàn là bắt buộc. Tiêm sẽ xảy ra tự động, vì vậy Mockito nên cư xử khá miễn cưỡng để tránh làm những việc xấu.Ngoài ra nếu tiêm phức tạp thì đối tượng của bạn quá phức tạp hoặc phải theo một mẫu tạo khác (ví dụ như một nhà xây dựng Joshua Bloch) – Brice

+0

Đây là vấn đề đã biết và mã trên của bạn là một công việc xung quanh khi các giao diện giả mạo cùng loại tồn tại trong hệ thống đang được kiểm tra. – alltej

0

Điều này được ghi lại trong mockito là công việc xung quanh, nếu nhiều mocks tồn tại cùng loại. Nó không giải quyết việc triển khai dựa trên tên được cung cấp (ví dụ: @Mock(name = "b2")). Thuật toán nó sử dụng để giải quyết việc thực hiện là bởi tên trường của phụ thuộc được tiêm. Vì vậy, mã của bạn ở trên sẽ giải quyết chính xác (b2 =>@Mock private B b2b3 =>@Mock private B b3).

Cách giải quyết khác là sử dụng chức năng tiêm xây dựng, đây là cách được khuyến cáo để tiêm phụ thuộc.

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