我正在学习使用Mockito和Robolectric在Android上进行测试。我使用Clean Architecture在RotJava和Dagger2中使用Kotlin创建了非常简单的应用程序。一切都在设备上运行良好,但我不能让我的测试通过。这是我的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),但我找不到它是什么。有谁知道可能导致这种情况的原因?
答案 0 :(得分:1)
您正在使用GetCredentials
(@Mock var mockCredentialsUseCase: GetCredentials
)的模拟实例,这就是您在其字段中null
的原因。除了被测试的主要类别(LoginPresenter
)之外,模仿一切并不是一个好主意。想到这一点的一种方法是将依赖关系划分为peers and internals。我会将测试重写为:
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中所述)。最重要的是尽量避免像this这样的测试 - 在我看来,它作为回归测试几乎没有增加价值,但仍然阻碍了重构。
PS。 Roboelectric添加helper functions for context