How to use mockk with a generic constructor param? - android

So I have a class
class Test<T : SomeListener> #Inject constructor(
private val dependency1: Dependency1,
private val listener: T
)
I'm trying to write a unit test for it using mockk and running into an error when trying to mock and initialize it with the generic type.
class TestTest {
#MockK
lateinit var dependency1: Dependency1
#MockK
lateinit var listener: ListenerImpl
#InjectMockKs(overrideValues = true)
lateinit var testObject: Test<ListenerImpl>
}
I keep getting an error "io.mockk.MockKException: No matching constructors found: ... listener : T =
What is the right way to get it to mock the constructor correctly with this generic parameter value?

Unfortunately I wasn't able to figure out a way to purely do it with annotations. What I ended up doing was:
class TestTest {
#MockK
lateinit var dependency1: Dependency1
#MockK
lateinit var listener: ListenerImpl
lateinit var testObject: Test<SomeListener>
#BeforeEach
fun setUp() {
testObject = Test(
dependency1,
listener
)
}
}
This worked and initialized the testObject correctly.

Related

lateinit property fakeAuthRepository has not been initialized unit testing

I am using unit testing when I run test I am getting following exception
ateinit property fakeAuthRepository has not been initialized
kotlin.UninitializedPropertyAccessException: lateinit property fakeAuthRepository has not been initialized
below my ViewModel test where test giving an exception
internal class SignInViewModelTest{
private val _login = MutableStateFlow<UiStateObject<SignInResponse>>(UiStateObject.EMPTY)
#Mock
lateinit var backendApi:BackendApi
lateinit var fakeAuthRepository: FakeAuthRepository
lateinit var authRepository: AuthRepository
private lateinit var viewModel: SignInViewModel
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
fakeAuthRepository = FakeAuthRepository(backendApi)
authRepository = AuthRepository(backendApi)
viewModel = SignInViewModel(authRepository)
}
var login = _login
#Test
fun `testing repository`() = runBlockingTest {
val fake = fakeAuthRepository.login("kyodgorbek#gmail.com", "12345678", "android", "123455")
val real = authRepository.login("kyodgorbek#gmail.com", "12345678", "android", "123455")
assertEquals(fake, real)
}
}
#Before is junit4 annotation.
If you are using junit5 you have to replace it with #BeforeEach.

Mockito does'nt mock repository

i'm try to test my ViewModel with mockito.
This is my TestClass:
#RunWith(JUnit4::class)
class RatesViewModelTest {
#Rule #JvmField
open val instantExecutorRule = InstantTaskExecutorRule()
#Mock
var observer: Observer<Pair<ArrayList<CurrencyExchangerModel>,Boolean>>? = null
#Mock
private lateinit var repository: RatesRepository
private var currencyList = ArrayList<CurrencyModel>()
private lateinit var viewModel : RatesViewModel
#Before
fun setUp(){
MockitoAnnotations.initMocks(this)
currencyList.add(CurrencyModel("BASE"))
viewModel = RatesViewModel(repository!!)
viewModel.getCurrencyExchangerObservableList().observeForever(observer!!)
}
#Test
fun testNull(){
Mockito.`when`(repository.getFlowableRates()).thenReturn( Flowable.just(currencyList) )
assertTrue(viewModel.getCurrencyExchangerObservableList().hasObservers())
}
}
When this method is invoked:
Mockito.`when`(repository.getFlowableRates()).thenReturn( Flowable.just(currencyList) )
I got this error:
kotlin.UninitializedPropertyAccessException: lateinit property db has
not been initialized
Here the repository:
open class RatesRepository(context:Context) : BaseRepository(context){
#Inject
lateinit var ratesAPI: RatesAPI
#Inject
lateinit var db: Database
/**
* load the updated chatList from API
*/
fun loadCurrencyRatesFromAPI(): Single<ArrayList<CurrencyModel>> {
val supportedCurrency = context.resources.getStringArray(R.array.currencies)
return ratesAPI.getLatestRates(EUR_CURRENCY_ID).map { RatesConverter.getRatesListFromDTO(it,supportedCurrency) }
}
/**
* save rates on DB
*/
fun saveCurrencyRatesOnDB(list:ArrayList<CurrencyModel>): Completable {
return db.currencyRatesDAO().insertAll(list)
}
/**
* get flawable rates from DB
*/
fun getFlowableRates(): Flowable<List<CurrencyModel>> {
return db.currencyRatesDAO().getAll()
}
companion object {
const val EUR_CURRENCY_ID = "EUR" //BASE
}
}
What i'm doing wrong ?
Thx !
When you define behaviour of a mock and use the when(...).then(...) notation of mockito,
the method itself is invoked (by mockito, normally not relevant for your test).
In your case that is a problem because db is not initialized.
To avoid this issues use the doReturn(...).when(...) syntax in these cases,
which does not cause the method invocation.
Mockito.doReturn(Flowable.just(currencyList)).when(repository).getFlowableRates();
(You might need to adjust this java syntax to make it kotlin compatible)

why mocked object function throws nulls instead of mocked data when using RobolectricTestRunner?

i have this test class running with MockitoJUnitRunner before and then i added RoboLectric, now i use RobolectricTestRunner
So the first thing it tryed is running my old tests but just changing the runner and the test now always fail. I do not really understand what is happening here, i am just trying to make my old test work with RobolectricTestRunner without stop using Mockito.
My Code before changing to RoboLectric (TEST PASS SUCCESSFULLY)
#RunWith(MockitoJUnitRunner::class)
class LauncherViewModelTest {
companion object {
#ClassRule
#JvmField
val schedulers = RxImmediateeSchedulerRule()
}
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
#Mock
private lateinit var mockContext: MyApplication
#Mock
private lateinit var mockedDatabase: MyDatabase
#Mock
private lateinit var session: Session
//
#Mock
lateinit var mockedDatabaseRxWrapper: DatabaseRxWrapper
/** Evaluated class **/
#InjectMocks
lateinit var launcherViewModel: LauncherViewModel
#Test
fun checkIfHasSessionSuccess() {
val sessionFlowable: Flowable<Session> = Flowable.fromCallable { session }
FieldSetter.setField(launcherViewModel,
launcherViewModel.javaClass.getDeclaredField("databaseRxWrapper"), mockedDatabaseRxWrapper)
doReturn(sessionFlowable).`when`(mockedDatabaseRxWrapper).getSession()
launcherViewModel.checkIfHasSession()
//$hasSession is a mutable live data
Assert.assertEquals(true, launcherViewModel.hasSession.value)
}
}
My Code after changing to RoboLectric : (DatabaseRxWrapper.getSession() returns always null even when i use Mockito.doReturn().when())
#RunWith(RobolectricTestRunner::class)
class LauncherViewModelTest {
companion object {
#ClassRule
#JvmField
val schedulers = RxImmediateeSchedulerRule()
}
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
#get:Rule
val mockitoRule = MockitoJUnit.rule()
#Mock
private lateinit var mockContext: MyApplication
#Mock
private lateinit var mockedDatabase: MyDatabase
#Mock
private lateinit var session: Session
//
#Mock
lateinit var mockedDatabaseRxWrapper: DatabaseRxWrapper
/** Evaluated class **/
#InjectMocks
lateinit var launcherViewModel: LauncherViewModel
#Test
fun checkIfHasSessionSuccess() {
val sessionFlowable: Flowable<Session> = Flowable.fromCallable { session }
FieldSetter.setField(launcherViewModel,
launcherViewModel.javaClass.getDeclaredField("databaseRxWrapper"), mockedDatabaseRxWrapper)
doReturn(sessionFlowable).`when`(mockedDatabaseRxWrapper).getSession()
launcherViewModel.checkIfHasSession()
//$hasSession is a mutable live data
Assert.assertEquals(true, launcherViewModel.hasSession.value)
}
}
Class under Test
class LauncherViewModel(application: Application) : AndroidViewModel(application) {
#Inject
lateinit var databaseRxWrapper: DatabaseRxWrapper
val hasSession: MutableLiveData<Boolean> = MutableLiveData()
val application by lazy { getApplication<MyApplication>() }
init {
application.getAppComponent()?.inject(this)
}
fun saveLocation(location: Location) = sharedPreferenceManager.saveLocation(location)
fun checkIfHasSession() {
databaseRxWrapper.getSession()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
hasSession.postValue(true)
}, {
hasSession.postValue(false)
})
}
}
I found that you don't need the rule for rewriting the configuration of RX if you are running with Robolectric
So the code worked after deleting this classrule:
companion object {
#ClassRule
#JvmField
val schedulers = RxImmediateeSchedulerRule()
}

Android Presenter Testing IllegalStateException captor.capture() must not be null

I have simple application based on mvp. Write test for presenter. Used Mockito for mock data. I catch view callback data (ArrayList) with ArgumentCaptor. My Test class
#RunWith(MockitoJUnitRunner::class)
class MainPresenterTest{
#Mock
lateinit var view:MainView
#Mock
lateinit var context:Context
#InjectMocks
lateinit var presenter: MainPresenter
#Captor
lateinit var captor: ArgumentCaptor<ArrayList<News>>
#Before
fun init(){
MockitoAnnotations.initMocks(this)
}
#Test
fun success(){
presenter.loadNews()
Mockito.verify<MainView>(view).onSuccess(captor.capture())
var data = captor.value
Mockito.verify(view).onSuccess(data)
Mockito.verify(view,never()).onError("")
}
}
Main View
interface MainView{
fun onSuccess(n:ArrayList<News>)
fun onError(e:String)
}
But throw
java.lang.IllegalStateException: captor.capture() must not be null
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
You actually don't need to define it as a class lateinit variable. In your test method, define a method variable like this
#Test
fun success(){
presenter.loadNews()
val captor: ArgumentCaptor<ArrayList<*>> = ArgumentCaptor.forClass(ArrayList::class.java)
Mockito.verify<MainView>(view).onSuccess(captor.capture())
var data = captor.value
Mockito.verify(view).onSuccess(data)
Mockito.verify(view,never()).onError("")
}
}
Also, you should assert the data from the captor. Instead of this
Mockito.verify(view).onSuccess(data)
do something like this
assertEquals("x", data.size())

How to implement lateinit properly with custom class objects for use in onCreate() in android avoiding 'Property getter or setter expected' error

I am trying to implement the integration of a Dialogflow (previously api.ai) agent with my Android app, using Kotlin. I checked other Q&A about kotlin lateinit and the onCreate() lifecycle in Android is ideal for late-init implementations, to avoid writing dirty code with null objects and corresponding !! and ? accesses in Kotlin. But I am running into the error of 'Property getter or setter expected' when trying to lateinint instances of self-defined class. Here is the code:
class AIApplication : Application() {
private var activitiesCount: Int = 0
var lateinit settingsManager: SettingsManager
//private set
private val isInForeground: Boolean
get() = activitiesCount > 0
override fun onCreate() {
super.onCreate()
settingsManager = SettingsManager(this)
}
Which gives me the error of 'Property getter or setter expected' on the lateinit settingsManager line. Here is the SettingsManager definition:
class SettingsManager(private val context: Context) {
private val prefs: SharedPreferences
private var useBluetooth: Boolean = false
var isUseBluetooth: Boolean
get() = useBluetooth
set(useBluetooth) {
this.useBluetooth = useBluetooth
prefs.edit().putBoolean(PREF_USE_BLUETOOTH, useBluetooth).commit()
val controller = (context.applicationContext as AIApplication).getBluetoothController()
if (useBluetooth) {
controller!!.start()
} else {
controller!!.stop()
}
}
init {
prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
useBluetooth = prefs.getBoolean(PREF_USE_BLUETOOTH, true)
}
companion object {
private val SETTINGS_PREFS_NAME = "ai.api.APP_SETTINGS"
private val PREF_USE_BLUETOOTH = "USE_BLUETOOTH"
}
}
So what is the proper way to do this? Do I need to make some changes to the SettingsManager class? Please explain the whole concept clearly.
The lateinit declaration of the SettingsManager is wrong. Try this :
lateinit var settingsManager: SettingsManager
instead of
var lateinit settingsManager: SettingsManager
Hope this helps.

Categories

Resources