Swift 高级测试:单元测试、集成测试和端到端测试
测试不仅仅是检查,更是可靠代码的基础。在高级水平上,Swift 开发者不仅要编写测试,还要巧妙地构建测试金字塔:从快速的单元测试到复杂的端到端(e2e)场景。在本文中,我们将深入探讨 iOS/macOS 项目的高级技术:依赖管理、CoreData 集成测试和 UI 自动化。
1. 单元测试:依赖管理和模拟
在高级水平上,单元测试必须是隔离的。关键技术是 依赖注入(DI)。我们不测试真实的网络层或数据库,而是用模拟对象来替代它们。
示例:协议和 DI
protocol NetworkServiceProtocol { func fetchData(completion: @escaping (Result<String, Error>) -> Void)}
class ViewModel { private let networkService: NetworkServiceProtocol init(networkService: NetworkServiceProtocol) { self.networkService = networkService } func loadData() { networkService.fetchData { result in // 处理结果 } }}
// 用于测试的模拟对象class MockNetworkService: NetworkServiceProtocol { var shouldReturnError = false func fetchData(completion: @escaping (Result<String, Error>) -> Void) { if shouldReturnError { completion(.failure(NSError(domain: "test", code: -1))) } else { completion(.success("Mock data")) } }}在测试中,您创建 MockNetworkService 并将其传递给 ViewModel。这允许您在不使用真实 API 的情况下测试错误和成功场景。
用于异步的 XCTestExpectation
func testViewModelLoadDataSuccess() { let mock = MockNetworkService() let viewModel = ViewModel(networkService: mock) let expectation = self.expectation(description: "Data loaded") viewModel.loadData() // 模拟异步调用 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { expectation.fulfill() } waitForExpectations(timeout: 1.0) // 检查 ViewModel 的状态}2. 集成测试:CoreData 和真实数据处理
集成测试检查组件之间的交互。对于 CoreData,我们使用 内存容器,以避免污染主数据库。
配置内存 CoreData
class CoreDataTestStack { lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "MyModel") let description = NSPersistentStoreDescription() description.type = NSInMemoryStoreType container.persistentStoreDescriptions = [description] container.loadPersistentStores { _, error in if let error = error { fatalError("Failed to load store: \\(error)") } } return container }() var context: NSManagedObjectContext { return persistentContainer.viewContext }}集成测试示例
func testSaveAndFetchUser() { let stack = CoreDataTestStack() let repository = UserRepository(context: stack.context) // 创建用户 let user = User(context: stack.context) user.name = "Test User" user.age = 30 try? stack.context.save() // 检查数据是否已保存 let fetchRequest: NSFetchRequest<User> = User.fetchRequest() let users = try? stack.context.fetch(fetchRequest) XCTAssertEqual(users?.count, 1) XCTAssertEqual(users?.first?.name, "Test User")}重要提示:每次测试后请清除上下文,以确保测试之间不会相互影响。