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
, ActivityComponent
và FragmentComponent
. 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()
và 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;
}
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
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
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