2016-11-03 22 views
5

Tôi đã xây dựng một ứng dụng ví dụ (Vâng, nó thực sự chỉ là một ví dụ và không có ý nghĩa nhiều nhưng tốt cho việc hiểu kiến ​​trúc sạch và sự phụ thuộc của Android trong Dagger 2). đang của tôi là có sẵn trên github. (lỗi thời. Xem this bưu điện) Các ứng dụng ví dụ chỉ cho phép của bạn gõ vào một tên trong một EditText và nếu bạn nhấn nút bạn sẽ thấy một thông báo "Xin chào Yourname"Dagger 2 Dependency Injection trong Android TestCase

tôi có ba khác nhau Các thành phần: ApplicationComponent, ActivityComponentFragmentComponent. FragmentComponent chứa ba mô-đun:

  • ActivityModule
  • FragmentModule
  • InteractorModule

InteractorModule cung cấp một MainInteractor.

@Module 
public class InteractorModule { 

    @Provides 
    @PerFragment 
    MainInteractor provideMainInteractor() { 
     return new MainInteractor(); 
    } 
} 

Trong Activity-UnitTest Tôi muốn giả mạo số MainInteractor. Interactor này chỉ có một phương thức public Person createPerson(String name) có thể tạo đối tượng Person. FakeMainInteractor có cùng phương thức nhưng luôn tạo đối tượng Person với tên „Fake Person“, không phụ thuộc vào tham số bạn đã truyền.

public class FakeMainInteractor { 
    public Person createPerson(final String name) { 
     return new Person("Fake Person"); 
    } 
} 

Tôi đã tạo thành phần TestComponents for evey Thành phần tôi mô tả ở trên. Và Trong TestFragmentComponent Tôi đổi chỗ InteractorModule với TestInteractorModule.

@PerFragment 
@Component(dependencies = TestApplicationComponent.class, modules = {ActivityModule.class, FragmentModule.class, TestInteractorModule.class}) 
public interface TestFragmentComponent { 
    void inject(MainFragment mainFragment); 

    void inject(MainActivity mainActivity); 
} 

Ví dụ này đang chạy tốt trong ngữ cảnh không kiểm tra. Trong số MainActivity Tôi có một phương pháp được gọi là initializeInjector() nơi tôi xây dựng FragmentComponent. Và onCreate() gọi onActivitySetup() mà gọi initializeInjector()inject().

public class MainActivity extends BaseActivity implements MainFragment.OnFragmentInteractionListener, 
     HasComponent<FragmentComponent> { 


    private FragmentComponent fragmentComponent; 
    private Fragment currentFragment; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     if (savedInstanceState == null) { 
      currentFragment = new MainFragment(); 
      addFragment(R.id.fragmentContainer, currentFragment); 
     } 

    } 


    private void initializeInjector() { 
     this.fragmentComponent = DaggerFragmentComponent.builder() 
       .applicationComponent(getApplicationComponent()) 
       .activityModule(getActivityModule()) 
       .fragmentModule(getFragmentModule()) 
       .build(); 
    } 

    @Override 
    protected void onActivitySetup() { 
     this.initializeInjector(); 
     fragmentComponent.inject(this); 

    } 

    @Override 
    public void onFragmentInteraction(final Uri uri) { 

    } 

    @Override public FragmentComponent getComponent() { 
     return fragmentComponent; 
    } 


    public FragmentModule getFragmentModule() { 
     return new FragmentModule(currentFragment); 
    } 
} 

Điều này làm việc tốt. Và MainActivityTest của tôi cũng hoạt động tốt. Nó kiểm tra việc gõ vào tên và kết quả của nút bấm sau. Nhưng TextView hiển thị „Hello John“.

public class MainActivityTest implements HasComponent<TestFragmentComponent> { 

    @Rule 
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class, true, true); 

    private MainActivity mActivity; 
    private TestFragmentComponent mTestFragmentComponent; 


    @Before 
    public void setUp() throws Exception { 
     mActivity = mActivityRule.getActivity(); 
    } 

    @Test 
    public void testMainFragmentLoaded() throws Exception { 
     mActivity = mActivityRule.getActivity(); 
     assertTrue(mActivity.getCurrentFragment() instanceof MainFragment); 
    } 

    @Test 
    public void testOnClick() throws Exception { 
     onView(withId(R.id.edittext)).perform(typeText("John")); 
     onView(withId(R.id.button)).perform(click()); 
     onView(withId(R.id.textview_greeting)).check(matches(withText(containsString("Hello John")))); 

    } 


    @Override 
    public TestFragmentComponent getComponent() { 
     return mTestFragmentComponent; 
    } 
} 

Nhưng như tôi đã nói tôi muốn sử dụng FakeMainInteractor sẽ in „Hello Fake Person“. Nhưng tôi không biết cách xây dựng biểu đồ phụ thuộc trong Bài kiểm tra. Vì vậy, trong chế độ thử nghiệm, tôi muốn một đồ thị khác được tạo ra, sử dụng các TestComponents và TestModules thay vì các thành phần và mô-đun ban đầu. Vậy làm thế nào để làm điều đó? Làm cách nào để thử nghiệm sử dụng FakeMainInteractor?

Như tôi đã nói, tôi biết ứng dụng mẫu này không làm gì hữu ích. Nhưng tôi muốn hiểu thử nghiệm với Dagger 2. Tôi đã đọc this bài viết. Nhưng nó chỉ cho thấy làm thế nào để làm cho TestComponents và TestModules. Nó không cho biết cách sử dụng Biểu đồ thử nghiệm trong Bài kiểm tra Đơn vị. Làm thế nào để làm điều đó? Ai đó có thể cung cấp một số mã ví dụ?

This không phải là giải pháp cho tôi, vì nó sử dụng và phiên bản cũ hơn của Dagger 2 (tôi sử dụng phiên bản 2.7) và không mô tả cách kết nối các thành phần thử nghiệm.

Sau khi cố gắng tiếp cận bởi @DavidRawson một số lớp học của tôi đã thay đổi việc thực hiện:

public class MainActivityTest{ 

    @Rule 
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class, true, true); 

    private MainActivity mActivity; 
    private TestApplicationComponent mTestApplicationComponent; 
    private TestFragmentComponent mTestFragmentComponent; 

    private void initializeInjector() { 
     mTestApplicationComponent = DaggerTestApplicationComponent.builder() 
       .applicationModule(new ApplicationModule(getApp())) 
       .build(); 

     getApp().setApplicationComponent(mTestApplicationComponent); 

     mTestFragmentComponent = DaggerTestFragmentComponent.builder() 
       .testApplicationComponent(mTestApplicationComponent) 
       .activityModule(mActivity.getActivityModule()) 
       .testInteractorModule(new TestInteractorModule()) 
       .build(); 

     mActivity.setFragmentComponent(mTestFragmentComponent); 

     mTestApplicationComponent.inject(this); 
     mTestFragmentComponent.inject(this); 

    } 

    public AndroidApplication getApp() { 
     return (AndroidApplication) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext(); 
    } 

    @Before 
    public void setUp() throws Exception { 
     mActivity = mActivityRule.getActivity(); 
     initializeInjector(); 
    } 

    @Test 
    public void testMainFragmentLoaded() throws Exception { 
     mActivity = mActivityRule.getActivity(); 
     assertTrue(mActivity.getCurrentFragment() instanceof MainFragment); 
    } 

    @Test 
    public void testOnClick() throws Exception { 
     onView(withId(R.id.edittext)).perform(typeText("John")); 
     onView(withId(R.id.button)).perform(click()); 
     onView(withId(R.id.textview_greeting)).check(matches(withText(containsString("Hello John")))); 
    } 

} 

MainActivity sở hữu phương pháp mới như sau:

@Override 
public void setFragmentComponent(final FragmentComponent fragmentComponent) { 
    Log.w(TAG, "Only call this method to swap test doubles"); 
    this.fragmentComponent = fragmentComponent; 
} 

AndroidApplication sở hữu:

public void setApplicationComponent(ApplicationComponent applicationComponent) { 
    Log.w(TAG, "Only call this method to swap test doubles"); 
    this.applicationComponent = applicationComponent; 
} 
+0

Dagger không được sử dụng để thử nghiệm. Trước tiên, bạn phải kiến ​​trúc các lớp của bạn để sử dụng DI, trong đó có tác dụng phụ tốt của việc làm cho lớp dễ dàng hơn để kiểm tra với hàng giả/mocks của các phụ thuộc. bạn có thể tạo mới các phụ thuộc theo cách thủ công hoặc sử dụng khung làm việc giả mạo. – Nkosi

+0

Kiến trúc của tôi đã sẵn sàng cho DI rồi. Tôi chỉ không thể áp dụng kiến ​​trúc cho trường hợp thử nghiệm. Chính xác những gì bạn có ý nghĩa của "mới lên các phụ thuộc bằng tay"? – unlimited101

+0

bạn tạo một thể hiện mới của 'FakeMainInteractor' và tiêm nó vào hệ thống đang được kiểm tra khi khởi tạo nó. Ngoài ra giả mạo của bạn và triển khai cụ thể nên chia sẻ một trừu tượng phổ biến. – Nkosi

Trả lời

2

Bạn có thể viết phương thức setter trong Application để ghi đè lên thư mục gốc Component

Sửa Application lớp hiện tại của bạn bằng cách thêm phương pháp này:

public class AndroidApplication extends Application { 

    @VisibleForTesting 
    public void setApplicationComponent(ApplicationComponent applicationComponent) { 
     Log.w(TAG, "Only call this method to swap test doubles"); 
     this.applicationComponent = applicationComponent; 
    } 
} 

bây giờ trong phương pháp thiết lập thử nghiệm của bạn, bạn có thể trao đổi các gốc thực Component với giả một:

@Before 
public void setUp() throws Exception { 
    TestApplicationComponent component = 
     DaggerTestApplicationComponent.builder() 
     .applicationModule(new TestApplicationModule()).build(); 

    getApp().setComponent(component); 

} 

private AndroidApplication getApp() { 
    return (AndroidApplication) InstrumentationRegistry.getInstrumentation() 
     .getTargetContext().getApplicationContext(); 
} 

Nếu bạn đang sử dụng các thành phần phụ thuộc, có thể bạn sẽ phải viết lại một phương thức có tên là setComponent bên trong số BaseActivity. Xin lưu ý rằng việc thêm các getters và setters công cộng có thể nói chung là thực hành thiết kế OO kém nhưng đây là giải pháp đơn giản nhất để thực hiện các phép thử kín bằng Dagger 2. Các phương pháp này được ghi thành here.

+1

Có 'setApplicationComponent()' thực sự cần trả về 'ApplicationComponent 'hoặc nó nên là' void'? –

+0

oops - phát hiện tốt! Tôi sẽ sửa nó –

+0

@DavidRawson Cảm ơn, giúp rất nhiều. Nhưng tiếc là thử nghiệm vẫn sử dụng 'MainInteractor' thay cho' FakeMainInteractor', mặc dù tôi đã thực hiện và gọi phương thức 'setComponent' này. Những gì tôi nghĩ là các thành phần được ghi đè bởi các TestComponents trong 'AndroidApplication' và' MainActivity' nhưng vẫn là những thành phần ban đầu được tiêm vào. Tôi đã đính kèm bản chỉnh sửa mới nhất vào bài đăng của mình. – unlimited101

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