【问题标题】:How to design an Android application using dagger with Testing in mind如何在考虑测试的情况下使用 Dagger 设计 Android 应用程序
【发布时间】:2014-04-20 00:54:32
【问题描述】:

我是 dagger 的新手,我最近开始在我自己的一个项目中使用 dagger,因为能够以不同方式处理依赖注入以进行测试和生产的概念,因此能够注入我可以注入的模拟对象用于测试很棒。

我修改了我的应用程序以遵循dagger simple-android example 中规定的样式。

设置完之后,我发现注入存在问题,我无法使用测试逻辑完全重载生产应用程序的注入。

我正在寻找有关如何设置它的建议,以使我的测试实际上可以根据需要注入不同的模拟或其他对象进行测试,而不是太笨拙。目前,MainActivityTest 已正确注入,但是当我们到达 MainActivity 时,它会转到 PhoneApplication 并使用它的对象图进行注入 p>

我在下面包含了我的内容。任何帮助将不胜感激!


这是我的 PhoneApplication,基于 DemoApplication

public class PhoneApplication extends Application {
    private ObjectGraph graph;

    @Override
    public void onCreate() {
        super.onCreate();

        graph = ObjectGraph.create(getModules().toArray());
    }

    protected List<Object> getModules() {
        return Arrays.asList(new AndroidModule(this), new PhoneModule());
    }

    public void inject(Object object) {
        graph.inject(object);
    }
}

这是我的 AndroidModule

@Module(library = true, injects = MainActivity.class)
public class AndroidModule {
    private final Context context;

    public AndroidModule(Context context) {
        this.context = context;
    }

    /**
     * Allow the application context to be injected but require that it be
     * annotated with {@link ForApplication @Annotation} to explicitly
     * differentiate it from an activity context.
     */
    @Provides
    @Singleton
    @ForApplication
    Context provideApplicationContext() {
        return context;
    }

    @Provides
    @Singleton
    NotificationManager provideNotificationManager() {
        return (NotificationManager) context
                .getSystemService(Application.NOTIFICATION_SERVICE);
    }

    @Provides
    @Singleton
    LocalBroadcastManager provideLocalBroadcastManager() {
        return LocalBroadcastManager.getInstance(context);
    }

    @Provides
    @Singleton
    ContentResolver provideContentResolver() {
        return context.getContentResolver();
    }

}

基于示例,我还将我的活动设置为使用基本活动。

public abstract class ActionBarBaseActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ((PhoneApplication) getApplication()).inject(this);
    }
}

然后在我的 MainActivity 我有以下

public class MainActivity extends ActionBarBaseActivity {

...

    @Inject
    LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
...
        try {
            messageReceivedIntentFilter = new IntentFilter(
                    Constants.EVENT_MESSAGE_RECEIVED,
                    "vnd.android.cursor.dir/vnd."
                            + DataProviderContract.AUTHORITY + "."
                            + DataProviderContract.MESSAGES_TABLE_NAME);

            localBroadcastManager.registerReceiver(messageReceiver,
                    messageReceivedIntentFilter);
        } catch (MalformedMimeTypeException e) {
            Log.e(LOG_TAG,
                    "An error occurred registering an Intent for EVENT_MESSAGE_RECEIVED",
                    e);
        }
...
    }
...
}

这效果很好,注射很快就到位了,我欣喜若狂。直到我真的想做一些测试。我想要执行的第一个测试是在我的 MainActivity 上。

在上面的 onCreate 方法中,我们使用 AndroidModule 中的 LocalBroadcastManager 注入,而不是 MainActivityTest 中的注入,因为我们目前没有办法告诉 PhoneApplication 或活动他们应该使用不同的对象图。

public class MainActivityTest extends
        ActivityInstrumentationTestCase2<MainActivity> {

    @Inject
    NotificationManager notificationManager;

    @Inject
    ContentResolver contentResolver;

    @Inject
    MockContentResolver mockContentResolver;

    @Inject
    LocalBroadcastManager localBroadcastManager;

    private Context context;

    public MainActivityTest() {
        super(MainActivity.class);
    }

    @Module(injects = { MainActivityTest.class, MainActivity.class }, library = true, overrides = true)
    static class MockModule {
        Context context;

        public MockModule(Context context) {
            this.context = context;
        }

        @Provides
        @Singleton
        ContentResolver provideContentResolver() {
            return provideMockContentResolver();
        }

        @Provides
        @Singleton
        MockContentResolver provideMockContentResolver() {
            return new MockContentResolver();
        }

        @Provides
        @Singleton
        LocalBroadcastManager provideLocalBroadcastManager() {
            return Mockito.mock(LocalBroadcastManager.class);
        }
    }

    @Override
    protected void setUp() throws Exception {
        System.setProperty("dexmaker.dexcache", getInstrumentation()
                .getTargetContext().getCacheDir().getPath());

        context = getInstrumentation().getTargetContext();
        ObjectGraph graph = ObjectGraph.create(new AndroidModule(context),
                new MockModule(context));
        graph.inject(this);

        super.setUp();
    };

    @MediumTest
    @UiThreadTest
    public void testIncomingMessageReceiver_onReceive()
            throws MalformedMimeTypeException {

        ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor
                .forClass(BroadcastReceiver.class);
        Mockito.verify(localBroadcastManager, Mockito.atLeastOnce())
                .registerReceiver(receiverCaptor.capture(),
                        Mockito.any(IntentFilter.class));
    }
}

这是一个让我开始的非常简单的测试。我知道在 onCreate 中,我们要注册一个 BroadcastReceiver,所以让我们确保它已注册。因为测试有mockLocalBroadcastManager,而activity使用的是生产LocalBroadcastManager,所以验证失败。

【问题讨论】:

  • 我不是仪器测试方面的专家。你能先检查一下叫什么 - setUp 用于测试或onCreate 用于活动吗?
  • 您确定 MainActivity 正在创建并且 onCreate() 正在被调用吗?根据文档,您必须从测试方法调用getActivity() 才能发生这种情况。另外,您是否注销了注入的广播接收器的类名并验证它确实使用的是生产配置,而不是提供模拟的覆盖配置?

标签: android unit-testing testing dependency-injection dagger


【解决方案1】:

我不确定。刚刚在网上搜索了如何正确使用 dagger 进行测试。

尽管如此,据我了解,MainActivity 从应用程序中获取其对象图。所以,这就是你必须插入MockModule 的地方。

为此,您应该创建PhoneApplication 的子类并覆盖getModules() 方法以返回您的MockModule。之后,您应该使用ActivityUnitTestCase.setApplication() 模拟应用程序(您的测试应该首先继承ActivityUnitTestCase)。这应该可以解决问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 2012-04-17
    相关资源
    最近更新 更多