MVVM模式单元测试:让无线组网应用更稳定

在开发智能家居相关的无线组网应用时,界面和逻辑的稳定性直接影响用户体验。比如你家里的Wi-Fi中继器管理App,点一下刷新却卡住,或者设备状态更新不及时,很可能就是背后代码没测到位。用MVVM(Model-View-ViewModel)模式写前端,能把界面和业务逻辑拆开,这对做单元测试特别友好。

为什么MVVM适合单元测试

MVVM把界面(View)和数据逻辑(ViewModel)分开,View只负责显示,所有操作交给ViewModel处理。这样一来,测试的时候不用启动整个界面,直接对ViewModel写测试用例就行。比如你添加一个新设备到网络组,只需要验证ViewModel里的方法是否正确调用了后台接口,而不需要真去点按钮、等页面渲染。

举个实际场景:你在App里点击“断开设备”,理想情况是设备从列表消失,并通知路由器更新连接状态。如果ViewModel里有个disconnectDevice(id)方法,测试时就可以模拟网络请求成功或失败,检查状态字段是否正确更新。

写一个简单的测试例子

假设你的无线组网App用的是Jetpack Compose + ViewModel,可以这样写测试:

class NetworkViewModelTest {

    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var viewModel: NetworkViewModel

    @Before
    fun setup() {
        viewModel = NetworkViewModel(FakeNetworkRepository())
    }

    @Test
    fun `断开设备后状态应标记为离线`() {
        viewModel.disconnectDevice("device_001")
        
        val state = viewModel.uiState.getOrAwaitValue()
        val device = state.devices.find { it.id == "device_001" }
        assertEqual(device?.status, DeviceStatus.OFFLINE)
    }
}

这里用了一个假的数据仓库FakeNetworkRepository,避免测试时真的去发网络请求。这样跑测试快,也更容易控制各种边界情况,比如网络超时、设备不存在等。

测试覆盖常见问题

在真实使用中,用户可能在网络不稳定时反复操作。比如连续点击“重启路由器”,ViewModel应该能正确处理重复调用,避免发送多个请求。这类逻辑可以在测试里模拟:

@Test
fun `重复调用重启应防止多次请求`() {
    var requestCount = 0
    val spyRepo = object : NetworkRepository {
        override fun restartRouter() {
            requestCount++
        }
    }
    viewModel = NetworkViewModel(spyRepo)

    viewModel.restartRouter()
    viewModel.restartRouter()

    assertEqual(requestCount, 1) // 防抖机制生效
}

这种测试能帮你提前发现逻辑漏洞,而不是等用户投诉“点了好几下才重启”才去修。

把核心逻辑都加上单元测试后,每次改代码都能快速验证有没有引入新问题。特别是在团队协作中,一个人改了设备排序逻辑,另一个人写的测试马上就能告诉你会不会影响到连接状态更新。