(DAGGER-ANDROID)不能在Espresso测试中使用@Inject,也不能使用mockWebServer。

问题描述 投票:1回答:4

我正在尝试创建Espresso测试,并使用一个 mockWebServer 问题是当我试图创建我的 mockWebServer 它调用真正的api调用,我想拦截它并模拟响应。

我的匕首组织是 。

我的应用

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:8080",我不知道是否需要其他东西。

另外我还创建了 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.我试图创建Espresso测试并使用一个mockWebServer,事情是当我试图创建我的mockWebServer时,它调用真正的api调用,我想拦截它并模拟响应。

android kotlin android-espresso dagger-2 dagger
4个回答
2
投票

你发布的设置看起来是正确的。至于 App 没有被提供,你可能需要在你的组件中绑定它,因为现在你绑定的是 TestApp 只有。所以你需要更换

fun create(@BindsInstance application: TestApp): TestAppComponent

fun create(@BindsInstance application: App): TestAppComponent

2
投票

我有同样的问题 mockWebServer 最近,你需要做的是放一个断点,看看是什么错误,在我的例子中,我把它放在我的 BaseRepository 的地方,发现异常是.NET的。

java.net.UnknownServiceException: CLEARTEXT communication to localhost not permitted by network security policy

为了解决这个问题,我的做法是在我的 manifest.xml

android:usesCleartextTraffic="true"

但你可能要用其他的方法,你可以看看在 android-8-cleartext-http-traffic-not-permitted(不允许)。.


1
投票

当我试图做一些类似的事情时,我没有创建两种类型的应用程序组件,只有一种。我为它们提供不同的输入,基于是否为实际的 AppTestApp. 不需要 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() 模式,但你已经明白了)。)

这里的模式是为你的app-component或其模块提供 "世界边缘 "的输入,这些东西需要根据app运行的上下文进行不同的配置(上下文如build-flavors,app运行在消费者设备上与运行在仪器模式下,等等)。例子如下 BuildConfig 值(如网络的base-urls)、真假硬件的接口实现、第三方lib的接口等。


1
投票

那么 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
}

And @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 之前。

此外,如果你想 @InjectTest Class,你可以添加。

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

然后,你可以调用:

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

来注入一些依赖关系到你的 TestClass.

希望能帮到你!


0
投票

我猜测你是想在你的TestApp类中注入OkHttpClient:

@Inject
lateinit var okHttpClient: OkHttpClient

在你的TestApp类中注入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类),因为它只知道要注入App类。如果你看一下AndroidInjector接口(你在AppComponent中使用的),你会发现它的声明是这样的。

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

这意味着它将在AppComponent中生成一个方法,

fun inject(app App)

在AppComponent中生成一个方法。这就是为什么@Inject在你的App类中工作,但在你的TestApp类中不起作用,除非你在TestAppComponent中明确提供了它。

© www.soinside.com 2019 - 2024. All rights reserved.