测试Android Kotlin应用程序 – 用匕首Mockito注入null

我正在学习使用Mockito和Robolectric在Android上进行测试。 我用RxJava和Dagger2在Kotlin中使用Clean Architecture创建了一个非常简单的应用程序。 一切正常,但我不能让我的测试通过。 这是我的LoginPresenterTest:

@RunWith(RobolectricGradleTestRunner::class) @Config(constants = BuildConfig::class) public class LoginPresenterTest { private lateinit var loginPresenter: LoginPresenter @Rule @JvmField public val mockitoRule: MockitoRule = MockitoJUnit.rule() @Mock private lateinit var mockContext: Context @Mock private lateinit var mockLoginUseCase: LoginUseCase @Mock private lateinit var mockLoginView: LoginView @Mock private lateinit var mockCredentialsUseCase: GetCredentials @Before public fun setUp() { loginPresenter = LoginPresenter(mockCredentialsUseCase, mockLoginUseCase) loginPresenter.view = mockLoginView } @Test public fun testLoginPresenterResume(){ given(mockLoginView.context()).willReturn(mockContext) loginPresenter.resume(); } } 

LoginPresenter构造器:

 class LoginPresenter @Inject constructor(@Named("getCredentials") val getCredentials: UseCase, @Named("loginUseCase") val loginUseCase: LoginUseCase) : Presenter<LoginView> 

loginPresenter.resume()我有:

 override fun resume() { getCredentials.execute(GetCredentialsSubscriber() as DefaultSubscriber<in Any>) } 

最后,GetCredentials:

 open class GetCredentials @Inject constructor(var userRepository: UserRepository, threadExecutor: Executor, postExecutionThread: PostExecutionThread): UseCase(threadExecutor, postExecutionThread) { override fun buildUseCaseObservable(): Observable<Credentials> = userRepository.credentials() } 

问题是, GetCredentials中的每个字段都是空的。 我想我错过了一些东西(我从这个项目中拿到了模式: https : //github.com/android10/Android-CleanArchitecture ),但我找不到它是什么东西。 有谁知道这是什么原因?

你正在使用GetCredentials@Mock var mockCredentialsUseCase: GetCredentials )的模拟实例,这就是为什么你的字段中@Mock var mockCredentialsUseCase: GetCredentials null 。 嘲讽所有与测试中的主要类( LoginPresenter )不同的是一个好主意。 想到这一点的一个方法是将依赖关系分为同辈和内部 。 我会重写测试像这样的:

 inline fun <reified T:Any> mock() : T = Mockito.mock(T::class.java) @RunWith(RobolectricGradleTestRunner::class) @Config(constants = BuildConfig::class) public class LoginPresenterTest { val mockContext:Context = mock() val mockLoginView:LoginView = mock().apply { given(this.context()).willReturn(mockContext) } val userRepository:UserRepository = mock() // or some in memory implementation val credentials = GetCredentials(userRepository, testThreadExecutor, testPostThreadExecutor) // yes, let's use real GetCredentials implementation val loginUseCase = LoginUseCase() // and a real LoginUseCase if possible val loginPresenter = LoginPresenter(credentials, loginUseCase).apply { view = mockLoginView } @Test public fun testLoginPresenterResume(){ given(mockLoginView.context()).willReturn(mockContext) loginPresenter.resume(); // do actual assertions as what should happen } } 

像往常一样,你需要考虑你正在测试的东西。 测试的范围不一定限于一个班级。 思考你正在测试的功能而不是类(比如在BDD中 )往往更容易。 最重要的是尽量避免这样的测试 – 在我看来, 这个测试增加了很小的回归测试的价值,但是仍然阻碍了重构。

PS。 Roboelectric 为上下文添加辅助函数