簡體   English   中英

如何使用匕首設計Android應用程序並考慮測試

[英]How to design an Android application using dagger with Testing in mind

我是匕首的新手,最近我開始在自己的一個項目中使用匕首,因為能夠以不同的方式處理依賴注入的概念用於測試和生產,因此能夠注入我可以用於測試的模擬對象很棒。

我修改了我的應用程序,以遵循dagger simple-android示例中的樣式。

設置完畢后,我發現注入存在問題,並且我無法使用測試邏輯完全超載生產應用程序中的注入。

我正在尋找關於如何設置它的建議,以便我的測試可以實際注入與模擬或其他對象的差異,以便根據需要進行測試,而不是過於愚蠢。 目前, MainActivityTest被正確注入,但是當我們到達MainActivity時 ,它會轉到PhoneApplication並使用它的對象圖注入

我已經把我下面的內容包括在內了。 任何幫助將不勝感激!


這是我的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中的LocalBroadcastManager,因為我們目前沒有辦法告訴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,但活動使用生產LocalBroadcastManager,因此驗證失敗。

我不確定。 剛剛通過網絡搜索了解如何正確使用匕首進行測試。

然而,據我所知, MainActivity從應用程序中獲取其對象圖。 所以,這是你必須插入MockModule

為此,您應該創建PhoneApplication的子類並覆蓋getModules()方法以返回您的MockModule 之后,您應該使用ActivityUnitTestCase.setApplication()模擬應用程序(您的測試應首先將ActivityUnitTestCase子類化)。 這應該解決問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM