【问题标题】:Testing Android Kotlin app - Mockito with Dagger injects null测试 Android Kotlin 应用程序 - 带有 Dagger 的 Mockito 注入 null
【发布时间】:2016-06-30 16:26:45
【问题描述】:

我正在学习使用 Mockito 和 Robolectric 在 Android 上进行测试。我使用 Clean Architecture 在 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 中的每个字段都是空的。我想我错过了一些东西(我从这个项目中获取了模式:https://github.com/android10/Android-CleanArchitecture),但我找不到它是什么。有谁知道这可能是什么原因?

【问题讨论】:

    标签: android mockito kotlin robolectric


    【解决方案1】:

    您正在使用 GetCredentials (@Mock var mockCredentialsUseCase: GetCredentials) 的模拟实例,这就是您在其字段中包含 nulls 的原因。除了被测主类 (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

    【讨论】:

    • 感谢您的回复。我通过创建 UseCaseTestClass 解决了这个问题,它模拟了 GetCredential 的行为,所以一切正常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 2018-03-08
    • 1970-01-01
    • 2023-03-04
    相关资源
    最近更新 更多