测试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 为上下文添加辅助函数