【问题标题】:What is the best way to inject subclasses using Dagger 2?使用 Dagger 2 注入子类的最佳方法是什么?
【发布时间】:2016-05-08 21:38:21
【问题描述】:

我最近开始使用 Dagger 2 来管理我的应用程序的依赖注入。为了使某些类可测试,我开始创建许多需要注入的类。我能够这样做,但是,注入这些新类的过程对我来说有点复杂。举个例子:

例如,我想测试我的 RecyclerViewAdapter 中的方法 onCreateViewHolder()。为此,我创建了一个工厂,它根据给定的 LayoutType 返回 ViewHolder:

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);
    }

}

通过将上面的代码移动到一个单独的类中,我可以使用以下方法执行我的单元测试:

@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);
    }

}

问题是:现在,为了使应用程序正常工作,我还必须注入保存因子实例的外部类(在创建 ViewHolderFactor 之前我没有这样做)。最后,我会有一个这样的 Dagger 配置:

@Module
public class ModuleBusiness {
    @Provides
    public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
        return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
    }
}

其中 CharacterUIService 是创建包含 ViewHolderFactory 的 RecyclerViewAdapter 的新实例的类。

public class 
    private ViewHolderFactor 
    @Inject
    public CharacterUIService(ViewHolderFactor viewHolderFactor) {
        this.mViewHolderFactor = viewHolderFactor;
    }

    // ...
}

我需要 ViewHolderFactor 的成员才能注入它。

我担心需要创建新的全局变量并增加构造函数中传递的参数数量。有没有更好的方法来注入这些子类,或者我可以将其视为允许单元测试的良好做法?

【问题讨论】:

  • 顺便说一句:“factor”这个词在英文中与“factory”相差甚远,因此您可以考虑更正您的代码措辞。
  • 你说得对,是打字失误。已经修复了

标签: android dependency-injection dagger dagger-2


【解决方案1】:

我可以将其视为允许单元测试的好习惯吗?

首先——在我个人看来——我认为你做的测试过度了。在您提供的测试用例中,您基本上是在测试 android 框架本身。如果你想测试工厂,你应该测试是返回一个小项还是大项,而不是视图不为空。

但是是的,您的方法似乎是允许进行单元测试的合理方法。

其中 CharacterUIService 是创建 RecyclerViewAdapter 新实例的类

您可能想过度考虑CharacterUIService 的角色。 “创建一个新实例”听起来应该由 dagger 处理,因为您已经在使用它了。

还必须将包含实例的外部类注入工厂

为什么? ViewHolderFactor(sic!) 本身没有依赖关系。即使是这样,是什么阻止了你用匕首创建和注入它?

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 并且您的 cmets 已经帮助了我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-15
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
相关资源
最近更新 更多