Тестирование приложения Android Kotlin – Mockito с кинжалом вводит нуль

Я изучаю тестирование на Android с Mockito и Robolectric. Я создал очень простое приложение в Kotlin с RxJava и Dagger2, используя чистую архитектуру. Все работает хорошо на устройстве, но я не могу пройти тест. Вот мой 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 равно null. Я думаю, что я что-то пропустил (я взял образец из этого проекта: https://github.com/android10/Android-CleanArchitecture ), но я не могу найти, что это такое. Кто-нибудь знает, что может вызвать это?

    Вы используете фиктивный экземпляр GetCredentials ( @Mock var mockCredentialsUseCase: GetCredentials ), поэтому у вас есть null в его полях. Редко бывает неплохо LoginPresenter все, кроме основного тестируемого класса ( 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 добавляет вспомогательные функции для контекста