【问题标题】:Field injection in unit tests with Dagger 2Dagger 2 单元测试中的字段注入
【发布时间】:2017-05-20 00:08:02
【问题描述】:

正如Dagger documentation 中所建议的那样,对于单元测试,我们根本不需要涉及 Dagger,对于提供的示例,它是有意义的:

class ThingDoer {
  private final ThingGetter getter;
  private final ThingPutter putter;

  @Inject ThingDoer(ThingGetter getter, ThingPutter putter) {
    this.getter = getter;
    this.putter = putter;
  }

  String doTheThing(int howManyTimes) { /* … */ }
}

使用这个类结构,单元测试很简单,只需模拟 getterputter,将它们作为构造函数参数传递,指示 mockito 在与任何这些对象交互时返回什么,然后对 @ 进行断言987654328@.

我在测试中苦苦挣扎的地方是当我必须对具有类似结构的类进行单元测试时:

class ThingDoer {
    @Inject
    ThingGetter getter;

    @Inject
    ThingPutter putter;

    @Inject
    ThingMaker maker;

    @Inject
    // other 10 objects

    public ThingDoer() {
        App.getThingComponent().inject(this);
    }

    String doTheThing(int howManyTimes) { /* … */ }
}

如您所见,我不再使用构造函数注入,而是使用字段注入。主要原因是构造函数没有太多参数。

ThingDoer 的所有依赖项都使用字段注入提供时,有没有办法在ThingDoer 中注入模拟依赖项?

【问题讨论】:

  • 我仍在尝试找出一个好的方法来做到这一点。一种可能的方法是:blog.sqisland.com/2015/04/dagger-2-espresso-2-mockito.html。我发现它不够灵活,因为它是所有测试的单一配置。顺便说一句,您试图做的是反对良好的 OO 设计 - 过多的依赖项通常意味着违反单一责任原则。不要尝试使用 Dagger 来“隐藏”它。这正是我在这篇文章中讨论的那种 DI 框架滥用:techyourchance.com/dependency-injection-android
  • 感谢您的评论。关于 DI 最佳实践的非常好的文章!最后,我将实现从字段更改为构造函数注入。不过,我有兴趣找到一种使用字段注入对类进行单元测试的正确方法。
  • 如果你这样做,请告诉我。你可能想看这个问题 (stackoverflow.com/questions/41139972/…) - 我问了一个不同的问题,但出于同样的原因,而且,由于除了我在上面的评论中发布的内容之外没有任何答案,我将尝试并如果我发现任何东西,请回帖。
  • 如果我错了,请纠正我,但不是所有这些字段都可以访问吗?因此,如果测试在同一个包中(它们通常是),您可以简单地执行thingDoer.getter = ... 等等,不是吗?唯一的问题是构造函数会尝试注入创建的对象......不确定你是否应该在构造函数中这样做
  • 嘿@Fred,你说得很好。我会试一试的。

标签: android unit-testing dagger-2


【解决方案1】:

对于字段注入,您可以创建用于单元测试的组件和模块。

假设你有单元测试类ThingDoerTest,你可以让组件将依赖注入ThingDoerTest而不是ThingDoer,并且模块提供模拟对象而不是真实对象。

在我的项目中,HomeActivity 有一个字段注入 HomePresenter。以下代码是一些sn-ps。希望代码能给你一些想法。

@RunWith(AndroidJUnit4.class)
public class HomeActivityTest implements ActivityLifecycleInjector<HomeActivity>{

    @Rule
    public InjectorActivityTestRule<HomeActivity> activityTestRule = new InjectorActivityTestRule<>(HomeActivity.class, this);

    @Inject
    public HomePresenter mockHomePresenter;

    @Override
    public void beforeOnCreate(HomeActivity homeActivity) {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        MyApplication myApplication = (MyApplication) instrumentation.getTargetContext().getApplicationContext();

        TestHomeComponent testHomeComponent = DaggerHomeActivityTest_TestHomeComponent.builder()
            .appComponent(myApplication.getAppComponent())
            .mockHomeModule(new MockHomeModule())
            .build();
        testHomeComponent.inject(HomeActivityTest.this);
        homeActivity.setHomeComponent(testHomeComponent);
    }

    @Test
    public void testOnCreate() throws Exception {
        verify(mockHomePresenter).start();
    }

    @ActivityScope
    @Component(
        dependencies = {
            AppComponent.class
        },
        modules = {
            MockHomeModule.class
        }
    )
    public interface TestHomeComponent extends HomeComponent {
        void inject(HomeActivityTest homeActivityTest);
    }

    @Module
    public class MockHomeModule {
        @ActivityScope
        @Provides
        public HomePresenter provideHomePresenter() {
            return mock(HomePresenter.class);
        }
    }

}

【讨论】:

    猜你喜欢
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 2015-07-11
    相关资源
    最近更新 更多