第7章:测试 MVVM 应用
Mock 数据与依赖注入
1. Mock 数据的作用与实现
在 MVVM 架构中,Mock 数据是测试 ViewModel 和 Model 逻辑的关键工具,它能够:
- 隔离依赖:避免测试受网络、数据库等外部服务影响。
- 提高测试速度:使用本地模拟数据替代耗时操作。
- 覆盖边界条件:模拟异常场景(如网络错误、空数据)。
实现方式(Swift 示例):
// 定义协议抽象网络服务
protocol TodoServiceProtocol {
func fetchTodos() async throws -> [Todo]
}
// 生产环境实现
class TodoService: TodoServiceProtocol {
func fetchTodos() async throws -> [Todo] {
// 实际网络请求代码
}
}
// Mock 实现
class MockTodoService: TodoServiceProtocol {
var mockTodos: [Todo] = []
var shouldFail = false
func fetchTodos() async throws -> [Todo] {
if shouldFail {
throw NetworkError.requestFailed
}
return mockTodos
}
}
2. 依赖注入(DI)在 MVVM 中的应用
依赖注入通过解耦组件依赖关系,使测试和维护更灵活。
常见模式:
构造函数注入(推荐):
class TodoViewModel { private let service: TodoServiceProtocol init(service: TodoServiceProtocol = TodoService()) { self.service = service } } // 测试时注入 Mock let mockService = MockTodoService() let viewModel = TodoViewModel(service: mockService)属性注入:
class TodoViewModel { var service: TodoServiceProtocol = TodoService() }
3. 结合 XCTest 的测试案例
class TodoViewModelTests: XCTestCase {
func testFetchTodosSuccess() async {
// 1. 准备 Mock
let mockService = MockTodoService()
mockService.mockTodos = [Todo(id: 1, title: "Test")]
// 2. 注入依赖
let viewModel = TodoViewModel(service: mockService)
// 3. 执行测试
await viewModel.loadTodos()
// 4. 验证结果
XCTAssertEqual(viewModel.todos.count, 1)
}
}
4. 最佳实践
- 协议抽象:为所有外部服务定义协议。
- 默认参数:生产代码使用默认实现,测试时覆盖。
- 集中管理:使用依赖容器(如
Swinject)管理复杂依赖。
案例扩展:在 Todo 应用中,可以通过 Mock 数据测试“空列表提示”或“网络错误弹窗”等 UI 逻辑,而无需真实触发 API。
