2016-09-12 16 views
5

Đối với một mô-đun dagger2Làm thế nào để Mock một đối tượng tiêm mà không được khai báo trong Module?

@Module 
public class MyModule { 
    @Provides @Singleton public RestService provideRestService() { 
     return new RestService(); 
    } 

    @Provides @Singleton public MyPrinter provideMyPrinter() { 
     return new MyPrinter(); 
    } 
} 

Chúng ta có thể có các mô-đun thử nghiệm như thử nghiệm

public class TestModule extends MyModule { 
    @Override public MyPrinter provideMyPrinter() { 
     return Mockito.mock(MyPrinter.class); 
    } 

    @Override public RestService provideRestService() { 
     return Mockito.mock(RestService.class); 
    } 
} 

Tuy nhiên nếu vì một lớp học như sau mà không được khai báo trong module dao găm ...

public class MainService { 
    @Inject MyPrinter myPrinter; 

    @Inject public MainService(RestService restService) { 
     this.restService = restService; 
    } 
} 

Làm cách nào để tạo mô hình MainService như trên. Lưu ý, tôi không có kế hoạch thực hiện thử nghiệm cho MainService theo chia sẻ trong https://medium.com/@fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke, nhưng thay vào đó, MainService của tôi được sử dụng trong một lớp bình thường khác mà tôi muốn thử nghiệm. ví dụ.

public class MyClassDoingSomething() { 
    @Inject MainService mainService; 

    public MyClassDoingSomething() { 
     //... 
    } 

    // ... 
    public void myPublicFunction() { 
     // This function uses mainService 
    } 
} 
+3

Hãy nhớ rằng việc sử dụng các thành phần Dagger trong _unit testing_ là không bình thường khi thẻ của bạn cho biết; để kiểm tra đơn vị, hãy tự gọi các nhà xây dựng và chuyển vào càng nhiều mock như bạn muốn. Đối với thử nghiệm _integration_, _system_ hoặc _end-to-end_, việc sử dụng thành phần Dagger thực sự có ý nghĩa hơn nhiều, nhưng sau đó nó cũng có ý nghĩa hơn nhiều khi sử dụng các phụ thuộc thực sự cho một thử nghiệm thực tế. –

+0

Cảm ơn @JeffBowman. Tôi có thể gây nhầm lẫn cho bạn về ý định của tôi do tên lớp. Tôi đã đổi tên MyClassToTest thành MyClassDoingSomething. vì đó không phải là một lớp Test, mà là một lớp thực tế. Bài kiểm tra đơn vị cho nó sẽ là một lớp khác không được định nghĩa trong câu hỏi của tôi ở trên. Trong trường hợp tôi đang thử nghiệm đơn vị một chức năng tức là myPublicFunction trong MyClassDoingSomething đang sử dụng mainService, tôi sẽ cần một gói mainService giả vào MyClassDoingSomething. Tôi biết làm thế nào để tiêm, nhưng không thể có được một giả của MyService tiêm thay vì đối tượng thực sự. – Elye

+0

Xin chào. Bạn có tìm thấy câu trả lời cuối cùng không? Tôi bị kẹt với cùng một vấn đề: https://stackoverflow.com/questions/45166990/mock-injected-viewmodel –

Trả lời

1

Điều này chắc chắn không trả lời câu hỏi của bạn, nhưng theo ý kiến ​​trung thực của tôi nó có liên quan, nó hữu ích và quá lớn để nhận xét.

Tôi thường phải đối mặt với câu hỏi này và tôi luôn luôn làm "tiêm phụ thuộc xây dựng". Điều này có nghĩa là tôi không còn làm tiêm lĩnh vực bằng cách chú thích lĩnh vực với @Inject nhưng vượt qua sự phụ thuộc vào các nhà xây dựng như sau:

public class MyClassDoingSomething implements DoSomethig { 
    private final Service mainService; 

    @Inject 
    public MyClassDoingSomething(Service mainService) { 
     this.mainService = mainService; 
    } 
} 

Thông báo như thế nào các nhà xây dựng hiện nay nhận được thông số và thiết lập các lĩnh vực để nó và là cũng được chú thích với @Inject? Tôi cũng muốn làm cho các lớp này thực hiện một giao diện (cũng cho MyService) - Trong số nhiều lợi ích khác mà tôi tìm thấy nó làm cho các mô-đun dao găm dễ dàng hơn để viết:

@Module 
public class DoSomethingModule { 
    @Provides @Singleton public RestService provideRestService() { 
     return new RestService(); 
    } 

    @Provides @Singleton public MyPrinter provideMyPrinter() { 
     return new MyPrinter(); 
    } 

    @Provides @Singleton public Service provideMyPrinter(MyService service) { 
     return service; 
    } 

    @Provides @Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) { 
     return something; 
    } 
} 

(này giả định rằng MyService dụng cụ hoặc kéo dài Service)

Bây giờ có vẻ như bạn đã biết rằng dao găm có thể tự mình tìm ra biểu đồ phụ thuộc và xây dựng tất cả các đối tượng cho bạn. Vì vậy, những gì về đơn vị kiểm tra lớp MyClassDoingSomething? Tôi thậm chí không dùng dao găm ở đây. Tôi chỉ cần cung cấp phụ thuộc theo cách thủ công:

public class MyClassDoingSomethingTest { 
    @Mock 
    Service service; 

    private MyClassDoingSomething something; 

    @Before 
    public void setUp() throws Exception { 
     MockitoAnnotations.init(this); 
     something = new MyClassDoingSomething(service); 
    } 
    // ... 
} 

Như bạn thấy, phụ thuộc được truyền qua phương thức khởi tạo thủ công.

Rõ ràng điều này không hoạt động nếu bạn đang mã hóa thứ gì đó không có hàm tạo có thể được bạn gọi. Ví dụ cổ điển là các hoạt động, đoạn hoặc lượt xem trên Android. Có nhiều cách để đạt được điều đó, nhưng cá nhân tôi vẫn nghĩ rằng bạn bằng cách nào đó có thể vượt qua điều này mà không cần dao găm. Nếu bạn là đơn vị thử nghiệm một quan điểm cho rằng có một trường @Inject MyPresenter myPresenter, thường là lĩnh vực này sẽ có quyền truy cập gói hoạt động tốt trong các bài kiểm tra:

public class MyViewTest { 
    @Mock MyPresenter presenter; 

    private MyView view; 

    @Before 
    public void setUp() throws Exception { 
     MockitoAnnotations.init(this); 
     view.myPresenter = presenter; 
    } 
} 

Lưu ý rằng đây chỉ hoạt động nếu cả hai MyViewTestMyView là trong cùng một gói (thường là trường hợp trong các dự án android).

Vào cuối ngày, nếu bạn vẫn muốn sử dụng dao găm cho các bài kiểm tra, bạn luôn có thể tạo ra "thử nghiệm" mô-đun và các thành phần có thể tiêm bằng cách tuyên bố phương pháp trong các thành phần như:

@Inject 
public interface MyTestComponent { 
    void inject(MyClassDoingSomething something); 
} 

tôi tìm cách tiếp cận này ok-ish, nhưng trong suốt những năm phát triển của tôi, tôi thích cách tiếp cận đầu tiên hơn. Điều này cũng đã báo cáo các vấn đề với Robolectric rằng một số thiết lập trong tệp build.gradle là bắt buộc để thực sự làm cho trình biên dịch dagger chạy cho các bài kiểm tra để các lớp thực sự được tạo.

+0

Cảm ơn Fred. Trong câu trả lời của bạn, bạn vẫn đang làm cho '@Provides @Singleton Public Service cung cấp dịch vụ MyService (dịch vụ MyService)' vào Module. Do đó nó không giải quyết được câu hỏi của tôi. Nhưng tôi đánh giá cao đầu vào của bạn và xây dựng từ một quan điểm khác. – Elye

+0

đúng vậy. Tôi đã cố gắng để cho thấy rằng tôi không sử dụng các mô-đun trong các bài kiểm tra, vì bạn có thể giả lập các phụ thuộc bằng cách gọi hàm tạo của '' MyPresenter'' trực tiếp. Trong ứng dụng sản xuất, bạn sẽ giữ các mô-đun. – Fred

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