Schlagwort: Android

#6 One thing to look out for when using LiveData and Transformations

If you were to unit test LiveData and LiveData transformations you might write a test like this:

class MainViewModel(initialArticle: Article) : ViewModel() {

    private val _articleLiveData = MutableLiveData<Article>()
    val articleLiveData: LiveData<Article> = _articleLiveData
    val articleTitleLiveData: LiveData<String> = Transformations.map(_articleLiveData) { it.title }

    init {
        _articleLiveData.value = initialArticle
    }
}

class MainViewModelTest {

    @get:Rule
    var rule: TestRule = InstantTaskExecutorRule()

    private lateinit var viewModel: MainViewModel

    private val initialArticle =
        Article("first Article", "text of first article. blablalbalb. blalbla. bla.")

    @Before
    fun setup() {
        viewModel = MainViewModel(initialArticle)
    }

    @Test
    fun testArticleEmission() {
        Assert.assertEquals(initialArticle, viewModel.articleLiveData.value)
    }

    @Test
    fun testArticleTitleEmission() {
        Assert.assertEquals(initialArticle.title, viewModel.articleTitleLiveData.value)
    }
}

But it might surprise you that the 2nd test testArticleTitleEmission will fail. Why is that?

Turns out that transformed LiveData does not emit values without an observer. So to make the test pass you need to adjust it in the following way:

@Test
fun testArticleTitleEmission() {
    viewModel.articleTitleLiveData.observeForever { } // This is the important bit!!
    Assert.assertEquals(initialArticle.title, viewModel.articleTitleLiveData.value)
}

Note: It doesn’t matter what your observer does, just that it exists.

Now both tests will pass.

#3 Android Switch Switching without triggering the listener

Sometimes we need to change the calue of a Switch (or SwitchMaterial), without calling the attached listener (to set it programmatically). To achieve this you can write yourself a little handy extension function like this:

fun CompoundButton.changeValueWithoutTriggeringListener(checked: Boolean, listener: CompoundButton.OnCheckedChangeListener) { 
    setOnCheckedChangeListener(null) 
    isChecked = checked 
    setOnCheckedChangeListener(listener) 
}

Unfortunately there is not getter to retrieve the listener, so you will have to store it in a variable and provide it later – or you could write yourself a little helper class like this:

class SwitchHelper(private val switch: CompoundButton) {
    private var listener: CompoundButton.OnCheckedChangeListener? = null

    fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener) {
        this.listener = listener
        switch.setOnCheckedChangeListener(listener)
    }

    fun changeValueWithoutTriggeringListener(checked: Boolean) {
        with(switch) {
            setOnCheckedChangeListener(null)
            isChecked = checked
            setOnCheckedChangeListener(listener)
        }
    }
}