Skip to content

Commit 8d1b21e

Browse files
committed
Add unit tests for NetworkConnectionMonitor
Extract the NetworkCallback logic into @VisibleForTesting onNetworkAvailable/ onNetworkLost so the connected-state model can be tested without the Android framework wiring in start(). Add NetworkConnectionMonitorTest covering: connected on first network, disconnected when the last network is lost, no false disconnect during a handover, de-dupe when unchanged, and re-emit on reconnect. Satisfies the Danger check requiring tests for the new class.
1 parent 622e7d7 commit 8d1b21e

2 files changed

Lines changed: 88 additions & 8 deletions

File tree

WordPress/src/main/java/org/wordpress/android/networking/NetworkConnectionMonitor.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.net.NetworkCapabilities
77
import android.net.NetworkRequest
88
import android.os.Handler
99
import android.os.HandlerThread
10+
import androidx.annotation.VisibleForTesting
1011
import androidx.lifecycle.LiveData
1112
import androidx.lifecycle.MutableLiveData
1213
import org.wordpress.android.util.AppLog
@@ -46,14 +47,8 @@ class NetworkConnectionMonitor @Inject constructor() {
4647

4748
val thread = HandlerThread("NetworkConnectionMonitor").apply { start() }
4849
val callback = object : ConnectivityManager.NetworkCallback() {
49-
override fun onAvailable(network: Network) {
50-
availableNetworks.add(network)
51-
onConnectivityChanged(availableNetworks.isNotEmpty())
52-
}
53-
override fun onLost(network: Network) {
54-
availableNetworks.remove(network)
55-
onConnectivityChanged(availableNetworks.isNotEmpty())
56-
}
50+
override fun onAvailable(network: Network) = onNetworkAvailable(network)
51+
override fun onLost(network: Network) = onNetworkLost(network)
5752
}
5853
val request = NetworkRequest.Builder()
5954
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -62,6 +57,18 @@ class NetworkConnectionMonitor @Inject constructor() {
6257
started = true
6358
}
6459

60+
@VisibleForTesting
61+
internal fun onNetworkAvailable(network: Network) {
62+
availableNetworks.add(network)
63+
onConnectivityChanged(availableNetworks.isNotEmpty())
64+
}
65+
66+
@VisibleForTesting
67+
internal fun onNetworkLost(network: Network) {
68+
availableNetworks.remove(network)
69+
onConnectivityChanged(availableNetworks.isNotEmpty())
70+
}
71+
6572
/**
6673
* Called on the monitor's background thread. Updates [isConnected] on the first callback and whenever the
6774
* connected state actually changes, so observers aren't spammed while connectivity churns.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.wordpress.android.networking
2+
3+
import android.net.Network
4+
import kotlinx.coroutines.ExperimentalCoroutinesApi
5+
import org.assertj.core.api.Assertions.assertThat
6+
import org.junit.Before
7+
import org.junit.Test
8+
import org.mockito.kotlin.mock
9+
import org.wordpress.android.BaseUnitTest
10+
11+
@ExperimentalCoroutinesApi
12+
class NetworkConnectionMonitorTest : BaseUnitTest() {
13+
private lateinit var monitor: NetworkConnectionMonitor
14+
private val emissions = mutableListOf<Boolean>()
15+
16+
@Before
17+
fun setUp() {
18+
monitor = NetworkConnectionMonitor()
19+
monitor.isConnected.observeForever { emissions.add(it) }
20+
}
21+
22+
@Test
23+
fun `emits connected when the first network becomes available`() {
24+
monitor.onNetworkAvailable(mock())
25+
26+
assertThat(monitor.isConnected.value).isTrue
27+
assertThat(emissions).containsExactly(true)
28+
}
29+
30+
@Test
31+
fun `emits disconnected when the only network is lost`() {
32+
val network = mock<Network>()
33+
monitor.onNetworkAvailable(network)
34+
monitor.onNetworkLost(network)
35+
36+
assertThat(monitor.isConnected.value).isFalse
37+
assertThat(emissions).containsExactly(true, false)
38+
}
39+
40+
@Test
41+
fun `does not report a disconnection during a network handover`() {
42+
val wifi = mock<Network>()
43+
val cellular = mock<Network>()
44+
45+
monitor.onNetworkAvailable(wifi)
46+
// the replacement network arrives before the old one is torn down
47+
monitor.onNetworkAvailable(cellular)
48+
monitor.onNetworkLost(wifi)
49+
50+
assertThat(monitor.isConnected.value).isTrue
51+
// only the initial connected emission; the handover is not a change
52+
assertThat(emissions).containsExactly(true)
53+
}
54+
55+
@Test
56+
fun `does not emit again while the connected state is unchanged`() {
57+
monitor.onNetworkAvailable(mock())
58+
monitor.onNetworkAvailable(mock())
59+
60+
assertThat(emissions).containsExactly(true)
61+
}
62+
63+
@Test
64+
fun `emits again when reconnecting after a full disconnect`() {
65+
val first = mock<Network>()
66+
monitor.onNetworkAvailable(first)
67+
monitor.onNetworkLost(first)
68+
monitor.onNetworkAvailable(mock())
69+
70+
assertThat(monitor.isConnected.value).isTrue
71+
assertThat(emissions).containsExactly(true, false, true)
72+
}
73+
}

0 commit comments

Comments
 (0)