androidunit-testingkotlin-delegate

How can a Delagetes.observable in a BroadcastReceiver be unit tested?


How can i test a Delegates.Observable that is inside a BroadcastReceiver. I need to get battery level of device and check if it's just went below or above pre-defined critical level, and upload to server using UseCase of clean architecture. I used observable to observe only changing states.

private fun handleIntent(context: Context, intent: Intent) {

    when (intent.action) {

 

        Intent.ACTION_BATTERY_CHANGED -> {

            try {
                val batteryStatus =
                    context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
                val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
                val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1

                 batteryPct = (level / scale.toFloat() * 100).toInt()

                isBatteryBelowCritical = batteryPct > CRITICAL_BATTERY

            } catch (e: Exception) {

            }

        }
    }
}

And observable

private var isBatteryBelowCritical by Delegates.observable(false) { _, old, new ->

        //has gone above critical battery value
        if (old && !new) {
            sendAlarmUseCase.sendBatteryAlarm(batteryPct)
        } else if (!old && new) {
            // has gone below critical battery value
            sendAlarmUseCase.sendBatteryAlarm(batteryPct)
        }

    }

Do i have to use parameters or assume old value to test current value? How is state is tested? Should i use parameterized test or assume previous value?


Solution

  • You could use a kind of dependency injection and refactor out the logic that checks for the state change:

    fun notifyOnlyOnChange(initialValue: Boolean, notify: () -> Unit): ReadWriteProperty<Any?, Boolean> =
        Delegates.observable(initialValue) { _, old, new ->
            if (old != new) // your logic can be simplified to this
                notify()
        }
    

    Then you can use it in your BroadcastReceiver like this:

    private var isBatteryBelowCritical by notifyOnlyOnChange(false) { 
        sendAlarmUseCase.sendBatteryAlarm(batteryPct)
    }
    

    And unit test it like this:

    
    @Test
    fun `test observers are not notified when value is not changed`() {
        var observable1 by notifyOnlyOnChange(false) { fail() }
        observable1 = false
        var observable2 by notifyOnlyOnChange(true) { fail() }
        observable2 = true
    }
    
    @Test
    fun `test observers are notified when value is changed`() {
        var notified1 = false
        var observable1 by notifyOnlyOnChange(false) { notified1 = true }
        observable1 = true
        assertTrue(notified1)
        var notified2 = false
        var observable2 by notifyOnlyOnChange(true) { notified2 = true }
        observable2 = false
        assertTrue(notified2)
    }