android - 使用 Dagger 2 注入(inject)子类的最佳方法是什么?

标签 android dependency-injection dagger dagger-2

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

例如,我想测试 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);
    }

}

问题是:现在,为了让应用程序正常工作,我还必须注入(inject)包含因子实例的外部类(在创建 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 的成员才能注入(inject)它。

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

最佳答案

can I consider it as a good practice in order to allow Unit Tests?

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

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

Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter

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

will also have to inject the outter class that holds an instance to the factory

为什么? ViewHolderFactor(原文如此!)本身没有依赖项。即使是这样,是什么阻止您用 Dagger 创建和注入(inject)它?

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 创建适配器?


一般来说,如果您支持构造函数注入(inject),那么您可以删除您的@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,因为您显然混淆了很多东西。好好看看构造函数注入(inject)并加以利用,它可以为您节省大量工作。

关于android - 使用 Dagger 2 注入(inject)子类的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37105098/

相关文章:

android - 房间 : How to use @Transaction in DAO Interface

android - Tasker 插件变量替换 : Developer

android - AdRequest isTestDevice 在 Android 8 的发布版本中返回 false

java - 对这个工厂感到困惑,因为它看起来既不像抽象工厂也不像工厂方法

android - 如何使用 Dagger 异步提供对象?

android - 无法将数据从 Activity 传递到 fragment

java - Spring单例初始化完成后如何运行方法?

java - 如何从 Guice 的注入(inject)器中检索带注释的实例?

android - Dagger 2 中 @Reusable 范围的用途是什么

android - Dagger2 一个模块用于两个不同的范围