Tash
10/14/2021, 7:00 PMsnapshotFlow
for unit testing state classes š§µ ā¬ļøTash
10/14/2021, 7:00 PMclass FooState {
val buttonIsVisible = mutableStateOf(false)
val buttonText = mutableStateOf("")
fun update() = { /** internally updates states **/ }
}
I know that we can use snapshotFlow
for unit testing the update
and the resultant changes to the `MutableState`s. But I would really like to take advantage of the by delegate and declare those `MutableState`s as vars
. Is there a way to āobserveā the state values in a unit test if by delegates were used here? Not sure if I am missing something obviousā¦Alex Vanyo
10/14/2021, 7:12 PMyschimke
10/14/2021, 8:39 PMTash
10/18/2021, 8:23 PMStateFlow
, i.e. one can update that multiple times, while `toList()`ing the incoming updates in a separate coroutine. then, finally asserting that the list contained all the expected updates.
with mutable/derivedStateOf
+ delegation, one could just run the function under test and assert the very next update (instead of asserting a slew of updates as with a flow/snapshotFlow).
b) there was a bug in my test š that I had to fix, after which using regular by delegation worked!
šš¼ thank you!
For completeness, heres parts of the state + test:
class CounterState(
initialCount: Int,
maxCount: Int
) {
private var counter by mutableStateOf(initialCount)
val isAtMax by derivedStateOf { counter == maxCount }
fun increment() { counter++ }
}
@Test // This passes!
fun `isAtMax - given incremented up to specified max, should return true`() {
val state = CounterState(initialCount = 0, maxCount = 1)
state.increment()
assertThat(state.isAtMax).isTrue()
}
Alex Vanyo
10/18/2021, 9:53 PMi was still in the mindset of how we model states usingĀThat mindset it still correct for Composeās state system, with the difference being in how you observe changes. You should still prefer to use, i.e. one can update that multiple times, whileĀ `toList()`ing the incoming updates in a separate coroutine. then, finally asserting that the list contained all the expected updates.StateFlow
snapshotFlow
in tests, so that you are also checking that your state changes will be observed properly by Compose.
In other words, right now you are testing that the value is correct when you query for it, not that a composable would be updated properly whenever it changes.
Concretely, that test wouldnāt be able to distinguish between these two implementations of `CounterState`:
class CounterState(
initialCount: Int,
private val maxCount: Int
) {
private var counter by mutableStateOf(initialCount)
val isAtMax get() = counter == maxCount
fun increment() { counter++ }
}
class CounterState(
initialCount: Int,
private val maxCount: Int
) {
private var counter = initialCount
val isAtMax get() = counter == maxCount
fun increment() { counter++ }
}
The first one will function equivalently to derivedStateOf
(and both of those have correct behavior), but the test would also pass for the second version. Because the second one doesnāt use mutableStateOf
, a composable that reads isAtMax
wonāt be recomposed whenever counter
changes.Tash
10/19/2021, 12:11 AMConcretely, that test wouldnāt be able to distinguish between these two implementations ofĀ `CounterState`:oh doh! yes indeed šš¼ makes sense, will make sure to use
snapshotFlow