使用 Dagger 2 注入子类的最佳方法是什么?
What is the best way to inject subclasses using Dagger 2?
我最近开始使用 Dagger 2 来管理我的应用程序的依赖注入。为了使一些 classes 可测试,我开始创建许多我需要注入的 classes。我能够这样做,但是,注入这些新 classes 的过程对我来说看起来有点复杂。让我们举个例子:
我想测试,例如,来自我的 RecyclerViewAdapter 的方法 onCreateViewHolder()
。为此,我创建了一个 returns ViewHolder 基于给定 LayoutType 的工厂:
public class ViewHolderFactor {
public RecyclerView.ViewHolder getViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = this.getLayoutInflater(parent.getContext());
View view;
switch (LayoutType.fromInteger(viewType)) {
case SMALL_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_small, parent, false);
return new ViewHolder.ItemViewHolder(view);
case LARGE_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_large, parent, false);
return new ViewHolder.ItemViewHolder(view);
}
return null;
}
private LayoutInflater getLayoutInflater(Context context) {
return LayoutInflater.from(context);
}
}
通过将上面的代码移动到一个单独的 class,我可以使用以下方法执行我的单元测试:
@RunWith(JUnit4.class)
public class TestViewHolderFactor extends TestCase {
ViewHolderFactor viewHolderFactor;
@Test
public void testGetViewHolder () {
this.viewHolderFactor = Mockito.mock(ViewHolderFactor.class);
ViewGroup viewGroup = Mockito.mock(ViewGroup.class);
Mockito.when(viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger())).thenCallRealMethod();
Context context = Mockito.mock(Context.class);
Mockito.when(viewGroup.getContext()).thenReturn(context);
LayoutInflater layoutInflater = Mockito.mock(LayoutInflater.class);
Mockito.when(viewHolderFactor.getLayoutInflater(context)).thenReturn(layoutInflater);
Mockito.when(layoutInflater.inflate(R.layout.rsc_util_item_small, viewGroup, false)).thenReturn(Mockito.mock(View.class));
RecyclerView.ViewHolder result = viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger());
assertNotNull(result);
}
}
问题是:现在,为了使应用程序正常工作,我还必须注入包含因子实例的外部 class(我在创建 ViewHolderFactor 之前没有这样做)。最后,我将有一个这样的 Dagger 配置:
@Module
public class ModuleBusiness {
@Provides
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
}
其中 CharacterUIService 是 class,它创建包含 ViewHolderFactory 的 RecyclerViewAdapter 的新实例。
public class
private ViewHolderFactor
@Inject
public CharacterUIService(ViewHolderFactor viewHolderFactor) {
this.mViewHolderFactor = viewHolderFactor;
}
// ...
}
我需要 ViewHolderFactor 的成员才能注入它。
我担心需要创建新的全局变量并增加构造函数中传递的参数数量。有没有更好的方法来注入这些子classes,或者我可以将其视为允许单元测试的良好做法吗?
can I consider it as a good practice in order to allow Unit Tests?
首先——在我个人看来——我认为你做得太过分了。在您提供的测试用例中,您基本上是在测试 android 框架本身 。如果要测试工厂,应该测试返回的是小项还是大项,而不是视图不为空。
但是,是的,您的方法似乎是允许进行单元测试的合理方法。
Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter
您可能想多想 CharacterUIService
的角色。 "that creates a new instance" 听起来应该由 dagger 来处理,因为你已经在使用它了。
will also have to inject the outter class that holds an instance to the factory
为什么? ViewHolderFactor
(原文如此!)本身没有依赖关系。即使它做到了,是什么阻止你用匕首创建和注入它?
class ViewHolderFactor {
@Inject
ViewHolderFactor() { // allow for constructor injection
}
}
class YourAdapter {
ViewHolderFactor mFactor;
@Inject // allow for constructor injection
YourAdapter (ViewHolderFactor factor) {/**/}
}
// now dagger knows how to create that adapter
然后创建让 dagger 创建那个适配器?
一般来说,如果您支持构造函数注入,那么您可以删除 @Provides providesSomething()
方法。只需删除整个方法。您在构造函数上有一个 @Inject
注释,因此请利用它并让 dagger 为您编写该代码。
// CharacterUIService can be constructor injected.
@Provides // DAGGER WILL DO THIS FOR YOU
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
// REMOVE ALL OF THIS.
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
因此,虽然我认为您在使事情可测试方面做得很好,但您应该再次好好看看 dagger,因为您显然混淆了很多东西。好好看看构造函数注入并利用它,它可以为您节省很多工作。
我最近开始使用 Dagger 2 来管理我的应用程序的依赖注入。为了使一些 classes 可测试,我开始创建许多我需要注入的 classes。我能够这样做,但是,注入这些新 classes 的过程对我来说看起来有点复杂。让我们举个例子:
我想测试,例如,来自我的 RecyclerViewAdapter 的方法 onCreateViewHolder()
。为此,我创建了一个 returns ViewHolder 基于给定 LayoutType 的工厂:
public class ViewHolderFactor {
public RecyclerView.ViewHolder getViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = this.getLayoutInflater(parent.getContext());
View view;
switch (LayoutType.fromInteger(viewType)) {
case SMALL_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_small, parent, false);
return new ViewHolder.ItemViewHolder(view);
case LARGE_VERTICAL:
view = inflater.inflate(R.layout.rsc_util_item_large, parent, false);
return new ViewHolder.ItemViewHolder(view);
}
return null;
}
private LayoutInflater getLayoutInflater(Context context) {
return LayoutInflater.from(context);
}
}
通过将上面的代码移动到一个单独的 class,我可以使用以下方法执行我的单元测试:
@RunWith(JUnit4.class)
public class TestViewHolderFactor extends TestCase {
ViewHolderFactor viewHolderFactor;
@Test
public void testGetViewHolder () {
this.viewHolderFactor = Mockito.mock(ViewHolderFactor.class);
ViewGroup viewGroup = Mockito.mock(ViewGroup.class);
Mockito.when(viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger())).thenCallRealMethod();
Context context = Mockito.mock(Context.class);
Mockito.when(viewGroup.getContext()).thenReturn(context);
LayoutInflater layoutInflater = Mockito.mock(LayoutInflater.class);
Mockito.when(viewHolderFactor.getLayoutInflater(context)).thenReturn(layoutInflater);
Mockito.when(layoutInflater.inflate(R.layout.rsc_util_item_small, viewGroup, false)).thenReturn(Mockito.mock(View.class));
RecyclerView.ViewHolder result = viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger());
assertNotNull(result);
}
}
问题是:现在,为了使应用程序正常工作,我还必须注入包含因子实例的外部 class(我在创建 ViewHolderFactor 之前没有这样做)。最后,我将有一个这样的 Dagger 配置:
@Module
public class ModuleBusiness {
@Provides
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
}
其中 CharacterUIService 是 class,它创建包含 ViewHolderFactory 的 RecyclerViewAdapter 的新实例。
public class
private ViewHolderFactor
@Inject
public CharacterUIService(ViewHolderFactor viewHolderFactor) {
this.mViewHolderFactor = viewHolderFactor;
}
// ...
}
我需要 ViewHolderFactor 的成员才能注入它。
我担心需要创建新的全局变量并增加构造函数中传递的参数数量。有没有更好的方法来注入这些子classes,或者我可以将其视为允许单元测试的良好做法吗?
can I consider it as a good practice in order to allow Unit Tests?
首先——在我个人看来——我认为你做得太过分了。在您提供的测试用例中,您基本上是在测试 android 框架本身 。如果要测试工厂,应该测试返回的是小项还是大项,而不是视图不为空。
但是,是的,您的方法似乎是允许进行单元测试的合理方法。
Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter
您可能想多想 CharacterUIService
的角色。 "that creates a new instance" 听起来应该由 dagger 来处理,因为你已经在使用它了。
will also have to inject the outter class that holds an instance to the factory
为什么? ViewHolderFactor
(原文如此!)本身没有依赖关系。即使它做到了,是什么阻止你用匕首创建和注入它?
class ViewHolderFactor {
@Inject
ViewHolderFactor() { // allow for constructor injection
}
}
class YourAdapter {
ViewHolderFactor mFactor;
@Inject // allow for constructor injection
YourAdapter (ViewHolderFactor factor) {/**/}
}
// now dagger knows how to create that adapter
然后创建让 dagger 创建那个适配器?
一般来说,如果您支持构造函数注入,那么您可以删除 @Provides providesSomething()
方法。只需删除整个方法。您在构造函数上有一个 @Inject
注释,因此请利用它并让 dagger 为您编写该代码。
// CharacterUIService can be constructor injected.
@Provides // DAGGER WILL DO THIS FOR YOU
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
// REMOVE ALL OF THIS.
return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}
因此,虽然我认为您在使事情可测试方面做得很好,但您应该再次好好看看 dagger,因为您显然混淆了很多东西。好好看看构造函数注入并利用它,它可以为您节省很多工作。