web-dev-qa-db-ja.com

(DAGGER-Android)Espressoテストで@Injectを使用できず、mockWebServerを使用できません

Espressoテストを作成してmockWebServerを使用しようとしています。実際のapi呼び出しを呼び出すmockWebServerを作成しようとすると、それをインターセプトして応答をモックしたいと思います。

私の短剣の組織は:

私のアプリ

open class App : Application(), HasAndroidInjector {

    lateinit var application: Application

    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector<Any>

    override fun androidInjector(): AndroidInjector<Any> = androidInjector

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.factory()
            .create(this)
            .inject(this)
        this.application = this
    }
}

次に、MyAppComponent

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        RetrofitModule::class,
        RoomModule::class,
        AppFeaturesModule::class
    ]
)
interface AppComponent : AndroidInjector<App> {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance application: App): AppComponent
    }
}

次に、このTestAppを作成しました

class TestApp : App() {

    override fun androidInjector(): AndroidInjector<Any> = androidInjector

    override fun onCreate() {
        DaggerTestAppComponent.factory()
            .create(this)
            .inject(this)
    }
}

そして、これは私のTestAppComponentです

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        TestRetrofitModule::class,
        AppFeaturesModule::class,
        RoomModule::class]
)
interface TestAppComponent : AppComponent {
    @Component.Factory
    interface Factory {
        fun create(@BindsInstance application: App): TestAppComponent
    }
}

注:ここでは、TestRetrofitModuleという新しいモジュールを作成しました。BASE_URLは " http:// localhost:808 "です。他に何か必要かどうかはわかりません。

また、TestRunnerを作成しました

class TestRunner : AndroidJUnitRunner() {

    override fun newApplication(
        cl: ClassLoader?,
        className: String?,
        context: Context?
    ): Application {
        return super.newApplication(cl, TestApp::class.Java.name, context)
    }

}

それをtestInstrumentationRunnerに置きます

問題1

使えない

@Inject
lateinit var okHttpClient: OkHttpClient

それは初期化されていないと言うので。

問題2(解決されたおかげでSkizo)

私のmockWebServerは、実際のapi呼び出しを指していなくても、応答をディスパッチしていません。TestRetrofitModuleに配置したものを指しているため、そのmockWebServerとRetrofitをリンクする必要があります。

7
StuartDTO

同様のことをしようとすると、2つのタイプのアプリケーションコンポーネントを作成するのではなく、1つだけ作成します。実際のAppに対するものかTestAppに対するものかに基づいて、さまざまな入力を提供します。 TestAppComponentはまったく必要ありません。例えば。

_open class App : Application(), HasAndroidInjector {

    lateinit var application: Application

    @Inject
    lateinit var androidInjector: DispatchingAndroidInjector<Any>

    override fun androidInjector(): AndroidInjector<Any> = androidInjector

    override fun onCreate() {
        super.onCreate()
        DaggerAppComponent.factory()
            .create(this, createRetrofitModule())
            .inject(this)
        this.application = this
    }

    protected fun createRetrofitModule() = RetrofitModule(BuildConfig.BASE_URL)
}

class TestApp : App() {
    override fun createRetrofitModule() = RetrofitModule("http://localhost:8080")
}


@Module
class RetrofitModule(private val baseUrl: String) {
    ...
    provide your Retrofit and OkHttpClients here and use the 'baseUrl'.
    ...
}
_

(これが「コンパイル」されるかどうかわからない。私は通常、builder()パターンではなくfactory()パターンをdagger-componentで使用しますが、あなたはアイデアを理解しています)。

ここでのパターンは、アプリコンポーネントまたはそのモジュールに、「エッジオブザワールド」への入力を提供することです。フレーバー、コンシューマーデバイスで実行されているアプリとインストルメンテーションモードで実行されているアプリなど)。例としては、BuildConfig値(ネットワーキングのベースURLなど)、実際のまたは偽のハードウェアへのインターフェース実装、サードパーティライブラリへのインターフェースなどがあります。

1

a dagger module あなたのための Test ClassContributeAndroidInjectorを入れ、Inject@Before 方法。

あなたのTestAppComponent

@Component(modules = [AndroidInjectionModule::class, TestAppModule::class])
interface TestAppComponent {
    fun inject(app: TestApp)

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: TestApp): Builder
        fun build(): TestAppComponent
    }
}

TestAppModuleのような:

@Module
interface TestAppModule {
    @ContributesAndroidInjector(modules = [Provider::class])
    fun activity(): MainActivity

    @Module
    object Provider {
        @Provides
        @JvmStatic
        fun provideString(): String = "This is test."

    }

    // Your other dependencies here
}

そして@BeforeのメソッドTest Classする必要があります:

@Before
fun setUp() {
    val instrumentation = InstrumentationRegistry.getInstrumentation()
    val app = instrumentation.targetContext.applicationContext as TestApp

    DaggerTestAppComponent.builder().application(app).build().inject(app)

   // Some things other
}

重要なことは、(build.gradleモジュールapp):

kaptAndroidTest "com.google.dagger:dagger-compiler:$version_dagger"
kaptAndroidTest "com.google.dagger:dagger-Android-processor:$version"

ActivityのようにMainActivityを起動すると、dependenciesではなくTestAppModuleからAppModuleが注入されます。

また、@InjectからTest Class、 あなたは付け加えられます:

fun inject(testClass: TestClass) // On Your TestAppComponent

そして、あなたは呼び出すことができます:

DaggerTestAppComponent.builder().application(app).build().inject(this) // This is on your TestClass

TestClassにいくつかの依存関係を注入します。

これがあなたを助けることを願っています!!

1
leo

私はあなたがOkHttpClientを注入しようとしていると思います:

@Inject
lateinit var okHttpClient: OkHttpClient

testAppクラスで、それは失敗します。これを機能させるには、TestAppComponentに注入メソッドを追加して、オーバーライドされたTestAppを注入し、次のようにする必要があります。

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        TestRetrofitModule::class,
        AppFeaturesModule::class,
        RoomModule::class]
)
interface TestAppComponent : AppComponent {
    @Component.Factory
    interface Factory {
        fun create(@BindsInstance application: App): TestAppComponent
    }

    fun inject(testApp: TestApp)
}

これが必要な理由は、Daggerが型ベースであり、注入時に提供される各クラスの型を使用して、コンパイル時にコードを生成する方法を決定するためです。あなたのケースでは、TestAppを挿入しようとすると、daggerはそのスーパークラス(Appクラス)を挿入します。 (AppComponentで使用する)AndroidInjectorインターフェースを見ると、次のように宣言されていることがわかります。

public interface AndroidInjector<T> {
    void inject(T instance)
....
}

つまり、メソッドが生成されます。

fun inject(app App)

appComponentで。これが@InjectがAppクラスで機能する理由ですが、TestAppComponentで明示的に指定しない限り、TestAppクラスでは機能しません。

0
gmetal