Android-CleanArchitecture icon indicating copy to clipboard operation
Android-CleanArchitecture copied to clipboard

Unable to test presenter ?

Open devendroid opened this issue 5 years ago • 9 comments

Hi, I have implemented the same architecture in kotlin but when I'm mocking a UseCase class, it's not initializing, my presenter test class is below:

   private var presenter:SetAddressPresenter? = null
   @Mock
   private var mockContext: Context? = null
   @Mock
   private var controller: Controller? = null
   @Mock
   private lateinit var setAddressUseCase: SetAddressUseCase

   @Before
   fun setUp() {
       MockitoAnnotations.initMocks(this)
       presenter = SetAddressPresenter(setAddressUseCase)
       presenter?.setAddressController(controller)
   }

   @Test
   fun testUserListPresenterInitialize() {
       given(controller?.context()).willReturn(mockContext)
       presenter?.loadPreviousAddresses()

       verify(controller)?.hideRetry()
       verify(controller)?.showLoading()
       verify(setAddressUseCase)?.execute(
           any(SetAddressPresenter.SetAddressObserver::class.java),
           any(Void::class.java)  )
    }

Getting the NullPointerException on ...domain.interactor.UseCase.execute(UseCase.kt:53)

my UseCase Class is:

..........
init {
        compositeDisposable = CompositeDisposable()
        this.threadExecutor = threadExecutor
        this.postExecutionThread = postExecutionThread
    }
fun execute(observer: DisposableObserver<T>, params: Any?) {
        val observable = this.buildUseCaseObservable(params)
            .subscribeOn(Schedulers.from(threadExecutor))   // Line no 53
            .observeOn(postExecutionThread.getScheduler())
        addDisposable(observable.subscribeWith(observer))
    }
..........

Could you please help or advice on this?

devendroid avatar Jan 16 '20 14:01 devendroid

You have to provide a mock to the actual call at the use case method inside the presenter.

rcfgnu avatar Jan 16 '20 16:01 rcfgnu

I have followed the same as this test class UserListPresenterTest

devendroid avatar Jan 17 '20 06:01 devendroid

Hi @rcfgnu , Have you found anything wrong/missing in my code from UserListPresenterTest class?

devendroid avatar Jan 18 '20 10:01 devendroid

@devendroid as I understood, NPE appeared because buildUseCaseObservable(params) method returned null.

Can you try the following to match types of the parameters in execute() method declaration:

verify(setAddressUseCase)?.execute(
           any(DisposableObserver.class),
           any(Params.class))

Also try to remove lateinit from setAddressUseCase, cause it is marked as lateinit, but I don't see where it is initialized. And make other mocks non nullable.

epetrenko avatar Jan 30 '20 10:01 epetrenko

Thanks, @epetrenko, to give the hint for NPE, I have resolved this, but now on verify execute method I'm getting this Exception:

E/TestRunner: org.mockito.exceptions.misusing.NotAMockException: 
    Argument passed to verify() is of type CountSurveyUseCase and is not a mock!
.........  

Now my test class is:

class SetAddressPresenterTest {    
    private lateinit var countSurveyUseCase: CountSurveyUseCase
    private lateinit var presenter:SetAddressPresenter

    @Mock private var mockContext: Context? = null
    @Mock private var controller: Controller? = null
    @Mock private lateinit var surveyRepository: SurveyRepository

    @Before
    fun setUp() {
        given(controller?.context()).willReturn(mockContext)
        given { surveyRepository.countSurvey() }.willReturn( Observable.empty() )

        countSurveyUseCase = CountSurveyUseCase(surveyRepository)
        presenter = SetAddressPresenter(countSurveyUseCase)
        presenter.setAddressController(controller!!)
    }

    @Test
    fun testSetAddressPresenter() {
        presenter?.checkOrdersInDraft()

        verify(surveyRepository).countSurvey()
        verifyNoMoreInteractions(surveyRepository)

        verify(countSurveyUseCase)?.execute(
            any(DisposableObserver::class.java) as DisposableObserver<Int>,
            any(Any::class.java))
    }
}

devendroid avatar Jan 31 '20 10:01 devendroid

@devendroid it happened because countSurveyUseCase is defined as lateinit var and initialized manually instead of using @Mock annotation. verify() method accepts only @Mock annotated parameters.

epetrenko avatar Feb 02 '20 12:02 epetrenko

@epetrenko I have mocked the countSurveyUseCase using @Mock annotation, but now I stuck on verify execute, Matchers.any( - ) not allowing generic types, showing a message "Only classes are allowed on the left-hand side of a class literal"

.......

verify(countSurveyUseCase)?.execute(
            any(DisposableObserver<Int>::class.java), // Showing syntax error on here
            any(Void::class.java))
......

devendroid avatar Feb 03 '20 11:02 devendroid

@devendroid try to do the following instead of defining generic type:

execute(any(DisposableObserver.class), any(Void.class))

epetrenko avatar Feb 03 '20 11:02 epetrenko

@epetrenko It's not allowing in kotlin, forcing to convert like this Void::class.java .

devendroid avatar Feb 03 '20 11:02 devendroid