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
に置きます
使えない
@Inject
lateinit var okHttpClient: OkHttpClient
それは初期化されていないと言うので。
私のmockWebServerは、実際のapi呼び出しを指していなくても、応答をディスパッチしていません。TestRetrofitModuleに配置したものを指しているため、そのmockWebServerとRetrofitをリンクする必要があります。
同様のことをしようとすると、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など)、実際のまたは偽のハードウェアへのインターフェース実装、サードパーティライブラリへのインターフェースなどがあります。
a dagger module
あなたのための Test Class
にContributeAndroidInjector
を入れ、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
にいくつかの依存関係を注入します。
これがあなたを助けることを願っています!!
私はあなたが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クラスでは機能しません。