Dagger 2 - 在 activity 中注入字段
Dagger 2 - Inject fields in activity
在我开始之前,我已经阅读了很多教程,但每个教程都包含有关旧匕首的信息 - 使用现已弃用的 @builder
。我正在使用 @Factory
我有什么?
class LoginActivity : AppCompatActivity() {
@Inject
lateinit var authService: AuthService
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
....
}
}
//----------------
@Singleton
@Component(modules = [TestAppModule::class])
interface TestApplicationComponent : AndroidInjector<TestMyApplication> {
@Component.Factory
abstract class Builder : AndroidInjector.Factory<TestMyApplication>
}
//----------------
class TestMyApplication : MyApplication() {
override fun onCreate() {
super.onCreate()
JodaTimeAndroid.init(this)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerTestApplicationComponent.factory().create(this)
}
}
//----------------
@Singleton
open class AuthService @Inject constructor(
@AppContext val context: Context, private val authRemoteDataSource: AuthRemoteDataSource
) {
...
}
//----------------
class MockRunner : AndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitAll().build())
super.onCreate(arguments)
}
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestMyApplication::class.qualifiedName, context)
}
}
备注:
- 我给你看,AuthService 中的构造函数,因为它有超过 0 个参数
- 模拟跑步者应用我的
TestMyApplication
class
和测试类
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@Mock
lateinit var mockAuthService: AuthService
@Rule
@JvmField
val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)
@Before
fun beforeEach() {
MockitoAnnotations.initMocks(this)
Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())
println(mockAuthService.getUserSignedStatus(true)) //test
}
@Test
fun buttonLogin() {
activityRule.launchActivity(Intent())
onView(withText("Google")).check(matches(isDisplayed()));
}
}
我想要什么?
- 以最简单的方式将模拟的 AuthService
附加到 LoginActivity
我得到了什么?错误:
调用方法时:android.content.Context.getSharedPreferences
排队:
Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())
方法getSharedPreferences
在实际方法getUserSignedStatus
中被调用。 所以现在,我收到一个错误,因为Mockito.when
调用了真正的函数public。我认为,第二个问题是模拟的 AuthService
没有注入到 LoginActivity
所以您可能应该通过一个模块提供 AuthService
,一个用于普通应用程序,一个用于 android 测试,后者提供模拟版本。这意味着从 AuthService
class 中删除 Dagger 注释。我不使用 Component.Factory
,但这个示例应该足以供您用作指南。
在 androidTest
文件夹中:
创建测试模块:
// normal app should include the module to supply this dependency
@Module object AndroidTestModule {
val mock : AuthService = Mockito.mock(AuthService::class.java)
@Provides
@Singleton
@JvmStatic
fun mockService() : AuthService = mock
}
创建测试组件:
@Component(modules = [AndroidTestModule::class])
@Singleton
interface AndroidTestComponent : AndroidInjector<AndroidTestApp> {
@Component.Builder interface Builder {
@BindsInstance fun app(app : Application) : Builder
fun build() : AndroidTestComponent
}
}
创建测试应用程序:
class AndroidTestApp : DaggerApplication() {
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAndroidTestAppComponent.builder().app(this).build()
}
然后是亚军:
class AndroidTestAppJunitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, AndroidTestApp::class.java.canonicalName, context)
}
}
包含在 android 闭包中 Gradle :
testInstrumentationRunner "com.package.name.AndroidTestAppJunitRunner"
添加这些部门:
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
kaptAndroidTest "com.google.dagger:dagger-android-processor:$daggerVersion"
androidTestImplementation "org.mockito:mockito-android:2.27.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
然后测试:
@RunWith(AndroidJUnit4::class) class LoginActivityTest {
@Rule
@JvmField
val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)
@Before
fun beforeEach() {
Mockito.doReturn(NOT_SIGNED).`when`(AndroidTestModule.mock).getUserSignedStatus(ArgumentMatchers.anyBoolean()
}
@Test
fun buttonLogin() {
activityRule.launchActivity(Intent())
onView(withText("Google")).check(matches(isDisplayed()));
}
}
然后您的依赖项将通过生成的测试组件图提供给 LoginActivity
在我开始之前,我已经阅读了很多教程,但每个教程都包含有关旧匕首的信息 - 使用现已弃用的 @builder
。我正在使用 @Factory
我有什么?
class LoginActivity : AppCompatActivity() {
@Inject
lateinit var authService: AuthService
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
....
}
}
//----------------
@Singleton
@Component(modules = [TestAppModule::class])
interface TestApplicationComponent : AndroidInjector<TestMyApplication> {
@Component.Factory
abstract class Builder : AndroidInjector.Factory<TestMyApplication>
}
//----------------
class TestMyApplication : MyApplication() {
override fun onCreate() {
super.onCreate()
JodaTimeAndroid.init(this)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerTestApplicationComponent.factory().create(this)
}
}
//----------------
@Singleton
open class AuthService @Inject constructor(
@AppContext val context: Context, private val authRemoteDataSource: AuthRemoteDataSource
) {
...
}
//----------------
class MockRunner : AndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitAll().build())
super.onCreate(arguments)
}
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestMyApplication::class.qualifiedName, context)
}
}
备注:
- 我给你看,AuthService 中的构造函数,因为它有超过 0 个参数
- 模拟跑步者应用我的
TestMyApplication
class
和测试类
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@Mock
lateinit var mockAuthService: AuthService
@Rule
@JvmField
val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)
@Before
fun beforeEach() {
MockitoAnnotations.initMocks(this)
Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())
println(mockAuthService.getUserSignedStatus(true)) //test
}
@Test
fun buttonLogin() {
activityRule.launchActivity(Intent())
onView(withText("Google")).check(matches(isDisplayed()));
}
}
我想要什么?
- 以最简单的方式将模拟的 AuthService
附加到 LoginActivity
我得到了什么?错误:
调用方法时:android.content.Context.getSharedPreferences
排队:
Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())
方法getSharedPreferences
在实际方法getUserSignedStatus
中被调用。 所以现在,我收到一个错误,因为Mockito.when
调用了真正的函数public。我认为,第二个问题是模拟的 AuthService
没有注入到 LoginActivity
所以您可能应该通过一个模块提供 AuthService
,一个用于普通应用程序,一个用于 android 测试,后者提供模拟版本。这意味着从 AuthService
class 中删除 Dagger 注释。我不使用 Component.Factory
,但这个示例应该足以供您用作指南。
在 androidTest
文件夹中:
创建测试模块:
// normal app should include the module to supply this dependency
@Module object AndroidTestModule {
val mock : AuthService = Mockito.mock(AuthService::class.java)
@Provides
@Singleton
@JvmStatic
fun mockService() : AuthService = mock
}
创建测试组件:
@Component(modules = [AndroidTestModule::class])
@Singleton
interface AndroidTestComponent : AndroidInjector<AndroidTestApp> {
@Component.Builder interface Builder {
@BindsInstance fun app(app : Application) : Builder
fun build() : AndroidTestComponent
}
}
创建测试应用程序:
class AndroidTestApp : DaggerApplication() {
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
DaggerAndroidTestAppComponent.builder().app(this).build()
}
然后是亚军:
class AndroidTestAppJunitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, AndroidTestApp::class.java.canonicalName, context)
}
}
包含在 android 闭包中 Gradle :
testInstrumentationRunner "com.package.name.AndroidTestAppJunitRunner"
添加这些部门:
kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
kaptAndroidTest "com.google.dagger:dagger-android-processor:$daggerVersion"
androidTestImplementation "org.mockito:mockito-android:2.27.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
然后测试:
@RunWith(AndroidJUnit4::class) class LoginActivityTest {
@Rule
@JvmField
val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)
@Before
fun beforeEach() {
Mockito.doReturn(NOT_SIGNED).`when`(AndroidTestModule.mock).getUserSignedStatus(ArgumentMatchers.anyBoolean()
}
@Test
fun buttonLogin() {
activityRule.launchActivity(Intent())
onView(withText("Google")).check(matches(isDisplayed()));
}
}
然后您的依赖项将通过生成的测试组件图提供给 LoginActivity