Android TestCase中的Dagger 2依赖注入 [英] Dagger 2 Dependency Injection in Android TestCase

查看:343
本文介绍了Android TestCase中的Dagger 2依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经构建了一个示例应用程序(是的,它实际上只是一个示例,并没有多大意义,但有助于理解Dagger 2中的Android干净架构和依赖注入)。我的
代码可在 github 上找到。(已过时。请参阅this 帖子)示例应用程序只是让你在 EditText 中键入一个名字,如果按下按钮,你会看到一条消息Hello YourName

I have built up an example application (Yes, it is really just an example and doesn’t make much sense but is good for understanding Android clean architecture and dependency injection in Dagger 2). My code is available on github.(Outdated. See this post) The example app just let’s you type in a name in an EditText and if you press the Button you see a message "Hello YourName"

我有三个不同的组件: ApplicationComponent ActivityComponent FragmentComponent FragmentComponent 包含三个模块:

I have three different Components: ApplicationComponent, ActivityComponent and FragmentComponent. FragmentComponent contains three modules:


  • ActivityModule

  • FragmentModule

  • InteractorModule

InteractorModule 提供 MainInteractor

@Module
public class InteractorModule {

    @Provides
    @PerFragment
    MainInteractor provideMainInteractor () {
        return new MainInteractor();
    }
}

在我的Activity-UnitTest中我想伪造这个 MainInteractor 。这个Interactor只有一个方法 public Person createPerson(String name),它可以创建一个Person对象。 FakeMainInteractor 具有相同的方法,但始终创建​​一个名为假人的Person对象,与您传递的参数无关。

In my Activity-UnitTest I want to fake this MainInteractor. This Interactor just has a method public Person createPerson(String name) which can create a Person object. The FakeMainInteractor has the same method but always creates a Person object with the name „Fake Person", indepent of the parameter you have passed.

public class FakeMainInteractor {
    public Person createPerson(final String name) {
        return new Person("Fake Person");
    }
}

我已经为上面描述的evey组件I创建了TestComponents。并在 TestFragmentComponent 我用 TestInteractorModule 交换了 InteractorModule

I already createt TestComponents for evey Component I described above. And In TestFragmentComponent I swapped InteractorModule with TestInteractorModule.

@PerFragment
@Component(dependencies = TestApplicationComponent.class, modules = {ActivityModule.class, FragmentModule.class, TestInteractorModule.class})
public interface TestFragmentComponent {
    void inject(MainFragment mainFragment);

    void inject(MainActivity mainActivity);
}

此示例在非测试环境中运行良好。在 MainActivity 中,我有一个名为 initializeInjector()的方法,其中我构建 FragmentComponent 。并且 onCreate()调用 onActivitySetup()哪个
调用 initializeInjector() inject()

This example is running well in a non-test context. In the MainActivityI have a method called initializeInjector() where I build the FragmentComponent. And onCreate()calls onActivitySetup() which calls initializeInjector() and 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);
    }
}

这很好用。我的 MainActivityTest 也可以。它测试名称的输入和以下按钮单击的结果。但 TextView 显示Hello John。

This works fine. And my MainActivityTestalso works fine. It tests the typing in of the name and the result of the following button click. But the TextView shows „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;
    }
}

但正如我告诉我想要使用 FakeMainInteractor 会打印Hello Fake Person。但我不知道如何在Test中构建依赖图。因此,在测试模式下,我想要使用TestComponents和TestModules而不是原始的组件和模块创建另一个图形。那怎么办呢?如何让测试使用 FakeMainInteractor

But as I told I want to use the FakeMainInteractor which would print „Hello Fake Person". But I don’t know how to build up the dependency graph within the Test. So in test mode I want another graph to be created, using the TestComponents and TestModules instead of the original Components and Modules. So how to do that? How to let the test use FakeMainInteractor?

正如我所说,我知道这个示例应用程序什么都不做有用。但我想了解Testing with Dagger 2.我已经阅读了这篇文章。但它只是展示了如何制作TestComponents
和TestModules。它没有说明如何在单元测试中使用测试图。怎么做?有人可以提供一些示例代码吗?

As I told, I know this example app doesn’t do anything useful. But I would like to understand Testing with Dagger 2. I already read this article. But it just shows how to make the TestComponents and the TestModules. It does not tell how to use a Test-Graph in the Unit Test. How to do that? Can someone provide some example code?

这个对我来说不是一个解决方案,因为它使用的是旧版本的Dagger 2(我使用的是2.7版本),并且描述了如何连接TestComponents。

This is not a solution for me, because it uses and older version of Dagger 2 (I use version 2.7) and it does not describe how to wire the TestComponents.

在尝试@DavidRawson后,我的一些课程改变了他们的实施:

After trying approach by @DavidRawson some of my classes changed their implementation:

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 拥有以下新方法:

@Override
public void setFragmentComponent(final FragmentComponent fragmentComponent) {
    Log.w(TAG, "Only call this method to swap test doubles");
    this.fragmentComponent = fragmentComponent;
}

AndroidApplication 拥有:

public void setApplicationComponent(ApplicationComponent applicationComponent) {
    Log.w(TAG, "Only call this method to swap test doubles");
    this.applicationComponent = applicationComponent;
}


推荐答案

你可以编写一个setter方法在应用程序中覆盖根组件

You can write a setter method in the Application to override the root Component

修改通过添加此方法,您当前的应用程序类:

Modify your current Application class by adding this method:

public class AndroidApplication extends Application {

    @VisibleForTesting
    public void setApplicationComponent(ApplicationComponent applicationComponent) {
        Log.w(TAG, "Only call this method to swap test doubles");
        this.applicationComponent = applicationComponent;
    }
}

现在在您的测试设置方法中,您可以交换真实的根组件与假的:

now in your test setup method, you can swap the real root Component with the fake one:

@Before
public void setUp() throws Exception {
    TestApplicationComponent component = 
      DaggerTestApplicationComponent.builder()
        .applicationModule(new TestApplicationModule()).build();

    getApp().setComponent(component); 

}

private AndroidApplication getApp() {
    return (AndroidApplication) InstrumentationRegistry.getInstrumentation()
      .getTargetContext().getApplicationContext();
}

如果您使用的是从属子组件,您可能需要再次写在 BaseActivity 中的一个名为 setComponent 的方法。请注意,添加公共getter和setter通常可能是糟糕的OO设计实践,但这是目前使用Dagger 2执行密封测试的最简单的解决方案。这些方法记录在案这里

If you are using dependent subcomponents, you will probably have to, again, write a method called setComponent inside your BaseActivity. Please note that adding public getters and setters can be, in general, bad OO design practice but this is currently the simplest solution for performing hermetic tests using Dagger 2. These methods are documented here.

这篇关于Android TestCase中的Dagger 2依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆