测试Android Kotlin应用程序 - Mockito与Dagger注入null

时间:2016-06-30 16:26:46

标签: android mockito kotlin robolectric

我正在学习使用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),但我找不到它是什么。有谁知道可能导致这种情况的原因?

1 个答案:

答案 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