3

I'm learning testing on Android with Mockito and Robolectric. I created very simple app in Kotlin with RxJava and Dagger2, using Clean Architecture. Everything works well on device, but I can't make my test pass. Here is my 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 contructor:

class LoginPresenter @Inject constructor(@Named("getCredentials") val getCredentials: UseCase,
                                     @Named("loginUseCase") val loginUseCase: LoginUseCase) : Presenter<LoginView>

in loginPresenter.resume() i have:

override fun resume() {
    getCredentials.execute(GetCredentialsSubscriber() as DefaultSubscriber<in Any>)
}

And, finally, GetCredentials:

open class GetCredentials @Inject constructor(var userRepository: UserRepository,
                                     threadExecutor: Executor,
                                     postExecutionThread: PostExecutionThread):
                                    UseCase(threadExecutor, postExecutionThread) {

    override fun buildUseCaseObservable(): Observable<Credentials> = userRepository.credentials()

}

The problem is, that every field in GetCredentials is null. I think I miss something (I took pattern from this project: https://github.com/android10/Android-CleanArchitecture), but I can't find what is it. Does anyone know what may cause this?

Tomasz Czura
  • 2,414
  • 1
  • 14
  • 18

1 Answers1

1

You're using a mock instance of GetCredentials (@Mock var mockCredentialsUseCase: GetCredentials) that's why you have nulls in its fields. It's rarely a good idea to mock everything apart from the main class under test (LoginPresenter). One way to think of this is to divide the dependencies into peers and internals. I would rewrite the test to something like:

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
    }
}

As usual you need to think about what you're testing. The scope of the test does not have to be limited to a single class. It's often easier to think of features you're testing instead of classes (like in BDD). Above all try to avoid tests like this - which in my opinion adds very little value as a regression test but still impedes refactoring.

PS. Roboelectric add helper functions for context

Community
  • 1
  • 1
miensol
  • 39,733
  • 7
  • 116
  • 112
  • Thank you for reply. I resolved this by creating `UseCaseTestClass`, which mocks `GetCredential` behaviour, so everything works good. – Tomasz Czura Jul 01 '16 at 07:59