为仪器测试注入espresso规则的依赖项

4
android studio 3.4.1
dagger-android 2.21

我正在使用dagger-android将OKHttpClient注入到espresso规则中。但是我还没有找到一种方法来做到这一点,我尝试了很多不同的方法。

这是我正在使用的规则,并且我正在尝试将okHttpClient注入其中。

class OkHttpIdingResourceRule(application: Application) : TestRule {

    /* My attempt below - but not working */
    private val testApplication =
        InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
                as AndroidTestGoWeatherApplication

    // private val testApplication = application.applicationContext as AndroidTestGoWeatherApplication
    private val component = testApplication.component as AndroidTestGoWeatherPresentationComponent
    private val okHttpClient: OkHttpClient = component.okHttpClient()

    private val idlingResource: IdlingResource = OkHttp3IdlingResource.create("okhttp", okHttpClient)

    override fun apply(base: Statement?, description: Description?): Statement {
        return object: Statement() {
            override fun evaluate() {
                IdlingRegistry.getInstance().register(idlingResource)
                base?.evaluate()
                IdlingRegistry.getInstance().unregister(idlingResource)
            }
        }
    }
}

这是我的AndroidTestGoWeatherApplication。
class AndroidTestGoWeatherApplication : GoWeatherApplication(), HasActivityInjector {
    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}

我的应用程序

open class GoWeatherApplication : Application(), HasActivityInjector, HasSupportFragmentInjector, HasServiceInjector {

    @Inject
    lateinit var dispatchingAndroidActivityInjector: DispatchingAndroidInjector<Activity>

    @Inject
    lateinit var dispatchingAndroidFragmentInjector: DispatchingAndroidInjector<Fragment>

    @Inject
    lateinit var dispatchingAndroidServiceInjector: DispatchingAndroidInjector<Service>

    lateinit var component: GoWeatherComponent

    override fun onCreate() {
        super.onCreate()

        component = DaggerGoWeatherComponent
            .builder()
            .application(this)
            .build()

            component.inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity> {
        return dispatchingAndroidActivityInjector
    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return dispatchingAndroidFragmentInjector
    }

    override fun serviceInjector(): AndroidInjector<Service> {
        return dispatchingAndroidServiceInjector
    }
}

我的主要应用程序组件

GoWeatherComponent
@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    ActivityBuilder::class,
    NetworkModule::class,
    GoWeatherApplicationModule::class])
interface GoWeatherComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: GoWeatherApplication): Builder

        fun build(): GoWeatherComponent
    }

    fun inject(application: GoWeatherApplication)
}

我的测试应用组件

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    TestNetworkModule::class,
    TestGoWeatherApplicationModule::class,
    TestForecastModule::class])
interface AndroidTestGoWeatherPresentationComponent : AndroidInjector<AndroidTestGoWeatherApplication> {

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<AndroidTestGoWeatherApplication>() {
        abstract fun applicationModule(TestApplicationModule: TestGoWeatherApplicationModule): Builder

        abstract fun testNetworkModule(testNetworkModule: TestNetworkModule): Builder
    }

    fun okHttpClient(): OkHttpClient
}

这是我的TestNetworkModule,我在其中创建了OkHttpClient。
@Module
class TestNetworkModule {

    @Singleton
    @Provides
    fun httpLoggingInterceptor(): HttpLoggingInterceptor {
        val loggingInterceptor = HttpLoggingInterceptor()

        loggingInterceptor.level = if(BuildConfig.DEBUG) {
            HttpLoggingInterceptor.Level.BODY
        }
        else {
            HttpLoggingInterceptor.Level.NONE
        }

        return loggingInterceptor
    }

    @Singleton
    @Provides
    fun provideOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(httpLoggingInterceptor)
            .connectTimeout(2, TimeUnit.SECONDS)
            .readTimeout(2, TimeUnit.SECONDS)
            .build()
    }

    @Named("TestBaseUrl")
    @Singleton
    @Provides
    fun provideBaseUrlTest(): String =
        "http://localhost:8080/"

    @Singleton
    @Provides
    fun provideRetrofit(@Named("TestBaseUrl") baseUrl: String, okHttpClient: OkHttpClient?): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient!!)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
    }
}

我的活动生成器。
@Module
abstract class ActivityBuilder {
    @ContributesAndroidInjector(modules = [ActivityModule::class])
    abstract fun injectIntoHomeActivity(): ForecastActivity

    @ContributesAndroidInjector(modules = [ActivityModule::class, ForecastModule::class])
    abstract fun injectIntoForecastFragment(): ForecastFragment
}

我的主Activity

class ForecastActivity : AppCompatActivity(), ForecastView, RetryListener, LocationUtilsListener {

    companion object {
        const val WEATHER_FORECAST_KEY = "weatherForecast"
    }

    @Inject
    lateinit var forecastPresenter: ForecastPresenter

    @Inject
    lateinit var location: LocationUtils

    private var fragmentManager: FragmentManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)
    }
}

我的仪表测试

@RunWith(AndroidJUnit4::class)
class ForecastActivityAndroidTest {
    @Inject
    lateinit var okHttpClient: OkHttpClient

    @get:Rule
    val okHttpIdingResourceRule = OkHttpIdingResourceRule(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as AndroidTestGoWeatherApplication)

    @get:Rule
    val activityRule = ActivityTestRule(ForecastActivity::class.java, false, false)

    private val mockWebserver: MockWebServer by lazy {
        MockWebServer()
    }

    @Before
    fun setUp() {
        val testApplication =
            InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
                    as AndroidTestGoWeatherApplication

        DaggerAndroidTestGoWeatherPresentationComponent
            .builder()
            .applicationModule(TestGoWeatherApplicationModule())
            .create(testApplication)
            .inject(testApplication)

        mockWebserver.start(8080)
    }

    @After
    fun tearDown() {
        mockWebserver.shutdown()
    }

    @Test
    fun should_load_five_day_forecast() {
        loadFromResources("json/fivedayforecast.json")
        mockWebserver.enqueue(MockResponse().setBody(loadFromResources("json/fivedayforecast.json")))

        ActivityScenario.launch(ForecastActivity::class.java)

       /* do some testing here * 
    }
}

Many thanks in advance


1
基本上我认为你从一开始就弄错了。根据你所说,你想用Espresso测试真实应用程序,并使用模拟来测试业务逻辑。因此,我会使用IdlingResource直到视图加载并显示来自真实服务器的实际天气数据。 - Kostas Drak
我正在使用 MockWebServer 来测试业务逻辑,但是我想要使用 IdlingResource,我的问题在于如何将其注入到 Espresso 测试中。 - ant2009
1个回答

2
我认为您在将OkHttpClient依赖注入到OkHttpIdingResourceRule的过程中,方法不太对。
来自Dagger2文档:

尽可能使用构造函数注入...

由于您拥有OkHttpIdingResourceRule,因此您应该在这里使用构造函数注入。
通过更改构造函数如下所示,允许Dagger为您构造OkHttpIdingResourceRule
class OkHttpIdingResourceRule @Inject constructor(application: Application, okHttpClient: OkHttpClient)

由于OkHttpClient已经在您的对象图中,因此我建议您将OkHttpIdingResourceRule注入到您的测试中,而不是OkHttpClient
话虽如此,我认为您仍然存在其他代码部分的问题,但如果没有运行它并亲眼看到错误,我无法确认它们。例如,如果您计划以这种方式注入测试,则必须在测试组件上拥有像这样的方法: void inject(ForecastActivityAndroidTest test); 编辑:
我再次查看了您的规则,发现您实际上真正感兴趣的是注入IdlingResource。如果是这样的话,您应该将构造函数更改为以下内容:
class OkHttpIdingResourceRule @Inject constructor(idlingRes: IdlingResource)

你可以在TestNetworkModule中创建一个提供方法来为你创建它:

最初的回答:

从那里,您可以在TestNetworkModule中创建一个提供方法来为您创建它:

@Provides 
IdlingResource providesIdlingResource(OkHttpClient okHttpClient {
    return OkHttp3IdlingResource.create("okhttp", okHttpClient)
}

嗨,感谢您的回复。我已经尝试实现了您建议的内容。基本上,我正在尝试在我的OkHttpIdingResourceRule中使用idlingResource来运行espresso测试。如果您有时间检查代码,我已经在这里添加了git存储库 https://github.com/steve1rm/GoWeather - ant2009

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接