2016-07-13 16 views
4

私は最近、簡単な依存性注入のためにAndroidアプリケーションにDagger2を実装しましたが、その後、いくつかのテストが機能しなくなりました。AndroidでDagger2 Dependency Injection&Robolectricを使ってテストするには?

Dagger2で動作するようにテストを調整する方法を理解しようとしていますか?私はテストを実行するためにRobolectricを使用しています。

ここではDagger2をどのように使用しているのですか?私はこれを最近学んだだけですので、これは悪い習慣であり、テストを助けるものではありません。

私は次のようであるAppModuleあります

@Module 
public class MyAppModule { 

    //Application reference 
    Application mApplication; 

    //Set the application value 
    public MyAppModule(Application application) { 
     mApplication = application; 
    } 

    //Provide a singleton for injection 
    @Provides 
    @Singleton 
    Application providesApplication() { 
     return mApplication; 
    } 
} 

そして、私は次のようである注射用オブジェクトを提供しNetworkModule呼び出す:

@Module 
public class NetworkModule { 

private Context mContext; 

//Constructor that takes in the required context and shared preferences objects 
public NetworkModule(Context context){ 
    mContext = context; 
} 

@Provides 
@Singleton 
SharedPreferences provideSharedPreferences(){ 
    //... 
} 

@Provides @Singleton 
OkHttpClient provideOKHttpClient(){ 
    //... 
} 

@Provides @Singleton 
Picasso providePicasso(){ 
    //... 
} 

@Provides @Singleton 
Gson provideGson(){ 
    //... 
} 
} 

をそしてコンポーネントが似ていますこれは:

Singleton 
@Component(modules={MyAppModule.class, NetworkModule.class}) 
public interface NetworkComponent { 

    //Activities that the providers can be injected into 
    void inject(MainActivity activity); 
    //... 
} 

私のテストでは、Robolectricを使用しています。私のApplicationクラスには、次のように:あなたは私が私のDagger2モジュールの嘲笑のバージョンが使用されていることを確認しようとしています見ることができるように

public class TestMyApplication extends TestApplication { 

    private static TestMyApplication sInstance; 
    private NetworkComponent mNetworkComponent; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     sInstance = this; 
     mNetworkComponent = DaggerTestMyApplication_TestNetworkComponent.builder() 
       .testMyAppModule(new TestMyAppModule(this)) 
       .testNetworkModule(new TestNetworkModule(this)).build(); 
    } 

    public static MyApplication getInstance() { 
     return sInstance; 
    } 

    @Override public NetworkComponent getNetComponent() { 
     return mNetworkComponent; 
    } 
} 

が、これらはTestMyApplicationと返す嘲笑NetworkModuleを返す嘲笑MyAppModuleと同様に嘲笑されています私は実際のNetworkComponentを拡張する虚偽のNetworkComponentも持っています。私はこのようなRobolectricを使用してアクティビティを作成、テストのセットアップで

//Build activity using Robolectric 
ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class); 
activity = controller.get(); 

controller.create(); //Create out Activity 

このアクティビティを作成してのonCreateを開始し、問題が発生したところ、これがある、のonCreateで私は、次のしていますそれはこのようDagger2を使用できるように、コンポーネントに活動を注入するコードの一部:

@Inject Picasso picasso; //Injected at top of Activity 

super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
ButterKnife.bind(this); 
MyApplication.getInstance().getNetComponent().inject(this); 

picasso.load(url).fetch(); 

ここでの問題は、テストを実行しているとき、私はピカソ変数にNullPointerExceptionが取得するということですので、私は私のDagger2のセットアップがあり推測しますどこかで欠けているリンクテスト?

EDIT:TestNetworkModule

@Module 
public class TestNetworkModule { 

    public TestNetworkModule(Context context){ 

    } 

    @Provides 
    @Singleton 
    SharedPreferences provideSharedPreferences(){ 
     return Mockito.mock(SharedPreferences.class); 
    } 


    @Provides @Singleton 
    Gson provideGson(){ 
     return Mockito.mock(Gson.class); 
    } 

    @Provides @Singleton 
    OkHttpClient provideOKHttpClient(){ 
     return Mockito.mock(OkHttpClient.class); 
    } 

    @Provides @Singleton 
    Picasso providePicasso(){ 
     return Mockito.mock(Picasso.class); 
    } 

} 
+0

あなたのTestNetworkModuleに何を追加できますか? – jbarat

+0

確かに、ちょうど今そこにいた –

+0

あなたのピカソ模造が呼ばれるときに何が起こるべきであるかを定義しますか?何かこのように:when(picassoMock.load(anyString()))thenReturn(something) – jbarat

答えて

6

TestApplicationおよびモジュールにセッターを追加する必要はありません。あなたはDagger 2を使用していますので、それを使ってテストに依存関係を注入する必要があります:

まず、MyApplicationでApplicationComponentを取得するメソッドを作成します。このメソッドは、TestMyApplicationクラスでオーバーライドされます。

public class MyApplication extends Application { 

    private ApplicationComponent mApplicationComponent; 

    public ApplicationComponent getOrCreateApplicationComponent() { 
     if (mApplicationComponent == null) { 
      mApplicationComponent = DaggerApplicationComponent.builder() 
        .myAppModule(new MyAppModule(this)) 
        .networkModule(new NetworkModule()) 
        .build(); 
     } 
     return mApplicationComponent; 
    } 
} 

はその後TestNetworkComponentを作成します。

@Singleton 
@Component(modules = {MyAppModule.class, TestNetworkModule.class}) 
public interface TestApplicationComponent extends ApplicationComponent { 
    void inject(MainActivityTest mainActivityTest); 
} 

TestNetworkModuleでは、あなたのTestMyApplicationでモック

@Provides 
@Singleton 
Picasso providePicasso(){ 
    return Mockito.mock(Picasso.class); 
} 

を返し、TestNetworkComponentを構築します:

public class TestMyApplication extends MyApplication { 

    private TestApplicationComponent testApplicationComponent; 

    @Override 
    public TestApplicationComponent getOrCreateApplicationComponent() { 
     if (testApplicationComponent == null) { 
      testApplicationComponent = DaggerTestApplicationComponent 
        .builder() 
        .myAppModule(new MyAppModule(this)) 
        .testNetworkModule(new TestNetworkModule()) 
        .build(); 
     } 
     return testApplicationComponent; 
    } 
} 

は、あなたのMainActivityTestにアプリケーションタグを実行して、依存関係を注入:

@RunWith(RobolectricGradleTestRunner.class) 
@Config(constants = BuildConfig.class, sdk = 21, application = TestMyApplication.class) 
public class MainActivityTest { 

    @Inject 
    Picasso picasso; 

    @Before 
    public void setup() { 
     ((TestMyApplication)RuntimeEnvironment.application).getOrCreateApplicationComponent().inject(this); 
     Mockito.when(picasso.load(Matchers.anyString())).thenReturn(Mockito.mock(RequestCreator.class)); 
    } 


    @Test 
    public void test() { 
     Robolectric.buildActivity(MainActivity.class).create(); 
    } 

} 

あなたのピカソのフィールドは今、あなたがそれと対話することができ、あなたのピカソのモックを注入されています。

+0

このようにすると、テストでMockito.whenを使用するとエラーが発生します。検証またはスタブの外では、引数マッチャーを使用することはできません。 引数マッチャーの正しい使用例: when(mock.get(anyInt()))。thenReturn(null); doThrow(新しいRuntimeException())。(モック).someVoidMethod(anyObject()); 確認(模擬)。someMethod(contains( "foo")) また、mockedできないメソッドで引数マッチャーを使用するため、このエラーが表示されることがあります。 以下のメソッド*はスタブ/検証できません:final/private/equals()/ hashCode() –

+0

実際には、あなたのコードに従った後、テストクラスから注入することはできません。「エラー:inject(MainActivityTest)の適切なメソッドが見つかりませんでした。私のDagger作成オブジェクトはこのようです:DaggerTestMyApplication_TestNetworkComponent –

+0

私は私の答えを完了しました。見てください@DonalRafferty –

1

を追加するだけでモックをバック与えることは十分ではありません。さまざまな通話のために返すべきものをあなたのモックに指示する必要があります。

私はあなたにピカソ模倣品の例を挙げていますが、それはすべての人にとって似ているはずです。 私はこれをTubeに書いているので、これを擬似コードとして扱います。

public class TestMyApplication extends TestApplication { 

    private static TestMyApplication sInstance; 
    private NetworkComponent mNetworkComponent; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     sInstance = this; 
    } 

    public void setModules(MyAppModule applicationModule, NetworkModule networkModule) { 
     this.applicationModule = applicationModule; 
     this.mNetworkComponent = DaggerApplicationComponent.builder() 
       .applicationModule(applicationModule) 
       .domainModule(networkModule) 
       .build(); 
    } 

    public static MyApplication getInstance() { 
     return sInstance; 
    } 

    @Override public NetworkComponent getNetComponent() { 
     return mNetworkComponent; 
    } 
} 

今、あなたはテストからあなたのモジュールを制御することができます:あなたはこのような外部の何かからモジュールを設定することができるように

はあなたTestMyApplicationを変更


次のステップでは、あなたのモックをアクセス可能にします。このようなもの:

@Module 
public class TestNetworkModule { 

    private Picasso picassoMock; 

    ... 

    @Provides @Singleton 
    Picasso providePicasso(){ 
     return picassoMock; 
    } 

    public void setPicasso(Picasso picasso){ 
     this.picasso = picasso; 
    } 
} 

これですべてのモックを制御できます。これですべてがテスト用に設定されている


ものを作ることができます:

@RunWith(RobolectricGradleTestRunner.class) 
public class PicassoTest { 

    @Mock Picasso picasso; 
    @Mock RequestCreator requestCreator; 

    @Before 
    public void before(){ 
     initMocks(this); 

     when(picassoMock.load(anyString())).thenReturn(requestCreator); 

     TestApplication app = (TestApplication) RuntimeEnvironment.application; 

     TestNetworkModule networkModule = new TestNetworkModule(app); 
     networkModule.setPicasso(picasso); 

     app.setModules(new TestMyAppModule(this), networkModule); 
     //Build activity using Robolectric 
     ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class); 
     activity = controller.get(); 
     activity.create(); 
    } 

    @Test 
    public void test(){ 
     //the test 
    } 

    @Test 
    public void test2(){ 
     //another test 
    } 
} 

だから今、あなたのテストを書くことができます。以前の設定はすべてのテストでこれを行う必要はありません。

関連する問題