Allow MockedViewObserver to return state object to improve testing
platramos opened this issue · comments
TLDR:
I would love to write assertions like this:
expectThat(view.state).isEqualTo(SomeState(param1, param2))
but the current implemenation of MockedViewObserver
prevents me from doing so.
The Problem
When testing the state of a viewmodel it is sometimes difficult to know which part of the state is incorrect and because MockedViewObserver
only exposes hasState
and hasEvent
Below is an example of the issues I am currently encountering. Given the following test:
@Before
fun setup() {
viewModel = SubmitIDStateViewModel()
view = viewModel.mockObservers()
}
@Test
fun `should mark state as valid if ID is valid`() {
private val fakeAddress = Faker.address.build()
viewModel.updateAddress(fakeAddress)
coVerify {
view.hasState(SubmitIDState(address= fakeAddress, id= ID(), isValid= true))
}
This test fails because isValid
never becomes true, but that is very unclear based on the error output:
java.lang.AssertionError: Verification failed: call 1 of 1: Observer(#3).onChanged(eq(SubmitIDState(address=Address(street=21 jump st), id=ID(), isValid=true)))). No matching calls found.
Calls to same method:
1) Observer(#3).onChanged(SubmitIDState(address=Address(street=), id=ID(id=default), isValid=false))
2) Observer(#3).onChanged(SubmitIDState(address=Address(street=21 jump st), id=ID(id=default), isValid=false))
3) Observer(#3).onChanged(SubmitIDState(address=Address(street=21 jump st), id=ID(id=), isValid=false))
One could argue this is a problem with Mockk and the way the verify
method handles parameter assertions, but the way MockedViewObserver
is implemented feels tightly coupled to the Mockk implementation because it only exposes the hasState
method and does not allow for other assertion libraries or approaches to validating internal data on the state object. This is why I would like for the state to be exposed in MockedViewObserver
so I could write assertions like this:
expectThat(view.state).isEqual(SubmitIDState(address= fakeAddress, id= ID() , isValid = true))
I looked a bit deeper into the Uniflow source code and was able to find a way to achieve my desired testing approach.
private lateinit var view: TestViewObserver
view = viewModel.createTestObserver()
assertThat(view.lastStateOrNull)
.isEqualToComparingFieldByFieldRecursively(
SubmitIDState(fakeAddress, ID(), true)
)
This approach wasn't immediately clear based on the docs, I can update them to surface the benefits of TestViewObserver
lastStateOrNull
and lastStateOrNull
if that's helpful.
This commit in open API to let your create the mock the way you want: 06a9e75
viewModel.mockObservers(stateObserverMock..., eventObserverMock)
Please reopen an issue if needed 👍