I'm writing a unit test. Below is my code. The architecture is MVVM using Dagger2. I'm calling the login function residing in the LoginViewModel, which is notifying the getLoginState function. The error I'm getting is:
Error:
io.mockk.MockKException: no answer found for: Observer(#8).onChanged(Success(data=))
at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:90)
LoginViewModelClass:
fun logIn(phone: String, phoneCode: String) {
loginState.value = Outcome.success("")
}
fun getLoginState(): LiveData<Outcome<String>> = loginState
LoginViewModelTest class:
@RelaxedMockK
var SUT: LoginViewModel? = null
@Mock
var loginInteractor: LoginInteractor? = null
@Mock
var textValidator: TextValidator? = null
@Mock
var textProvider: TextProvider? = null
@Mock
var blinkUserPreferences: BlinkUserPreferences? = null
@get:Rule
var rule: TestRule = InstantTaskExecutorRule()
@RelaxedMockK
var mockObserver: Observer<Outcome<String>>? = null
@Before
fun setUp() {
MockKAnnotations.init(this, relaxUnitFun = true)
SUT = spyk(
LoginViewModel(
mockk<LoginInteractor>(),
mockk<TextValidator>(relaxed = true),
mockk<TextProvider>(),
mockk<BlinkUserPreferences>()))
mockObserver = mockk<Observer<Outcome<String>>>()
SUT!!.getLoginState().observeForever(mockObserver!!)
}
@Test
fun logIn() {
//Arrange
every {SUT!!.getLoginState().value} returns Outcome.success("")
//Act
SUT!!.logIn("89989676","89998")
//Assert
verify() { mockObserver!!.onChanged(Outcome.success("abc")) }
}
Question: In verification, why onChanged method is not being called, or what does it mean that no answer found for Observer().onChanged, how can I notify my onChanged method so I can verify it?
After watching this: https://mockk.io/#answers. It says
specify that the matched call answers with a code block scoped with answer scope
I just posted this:
every { mockObserver!!.onChanged(any()) } answers {}
in the following test function and it worked.
@Test
fun logIn() {
//Arrange
every { mockObserver!!.onChanged(any()) } answers {}
every {SUT!!.getLoginState().value} returns Outcome.success("abc")
//Act
SUT!!.logIn("89989676","89998")
//Assert
verify() { mockObserver!!.onChanged(Outcome.success("abc")) }
}
According to my understanding, if you mockk
a function, and you want to use its particular function you must use the every
expression to tell framework that it will answer
, because framework needs to know that it needs to answer something.
And if you want that all behaviour functions should also be added with mock with their implementation then you must spyk
your class so that it gets the behaviour as well and then you can easily use the function without using expression every
.
Please note that every
expression is used for many cases like to get a mocked result out of that function, or just need to tell the framework that this function should answers
this.
Please correct me through comments if I'm wrong, Ill update it.