我正在尝试创建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
我不能用
@Inject
lateinit var okHttpClient: OkHttpClient
因为它说它没有被初始化。
我的mockWebServer没有调度响应,甚至--虽然不是指向真正的api调用,而是指向我放在TestRetrofitModule上的那个,事情是我必须链接那个mockWebServer和Retrofit.我试图创建Espresso测试并使用一个mockWebServer,事情是当我试图创建我的mockWebServer时,它调用真正的api调用,我想拦截它并模拟响应。
你发布的设置看起来是正确的。至于 App
没有被提供,你可能需要在你的组件中绑定它,因为现在你绑定的是 TestApp
只有。所以你需要更换
fun create(@BindsInstance application: TestApp): TestAppComponent
与
fun create(@BindsInstance application: App): TestAppComponent
我有同样的问题 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(不允许)。.
当我试图做一些类似的事情时,我没有创建两种类型的应用程序组件,只有一种。我为它们提供不同的输入,基于是否为实际的 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()
模式,但你已经明白了)。)
这里的模式是为你的app-component或其模块提供 "世界边缘 "的输入,这些东西需要根据app运行的上下文进行不同的配置(上下文如build-flavors,app运行在消费者设备上与运行在仪器模式下,等等)。例子如下 BuildConfig
值(如网络的base-urls)、真假硬件的接口实现、第三方lib的接口等。
那么 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
之前。
此外,如果你想 @Inject
到 Test Class
,你可以添加。
fun inject(testClass: TestClass) // On Your TestAppComponent
然后,你可以调用:
DaggerTestAppComponent.builder().application(app).build().inject(this) // This is on your TestClass
来注入一些依赖关系到你的 TestClass
.
希望能帮到你!
我猜测你是想在你的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中明确提供了它。