测试与调试
XCTest、Instruments、SwiftLint。
30 分钟
测试与调试
iOS 测试栈:XCTest 单元/UI 测试、Instruments 性能分析、SwiftLint 代码规范。对照 /roadmaps/ios「Testing & Debugging」节点。
学习目标
- 编写 XCTest 单元测试与 Mock 依赖
- 了解 XCUITest 基础与 UI 测试局限
- 用 Instruments 定位内存泄漏与 CPU 热点
- 对照 JUnit 与 React Testing Library
核心概念
XCTest 单元测试
import XCTest
@testable import CodeRoadMap
final class TrackListViewModelTests: XCTestCase {
var sut: TrackListViewModel!
var mockRepo: MockTrackRepository!
override func setUp() {
super.setUp()
mockRepo = MockTrackRepository()
sut = TrackListViewModel(repository: mockRepo)
}
override func tearDown() {
sut = nil
mockRepo = nil
super.tearDown()
}
func testLoadTracksSuccess() async {
mockRepo.stubbedTracks = [
Track(id: 1, title: "iOS", lessonCount: 12),
]
await sut.loadTracks()
XCTAssertEqual(sut.tracks.count, 1)
XCTAssertNil(sut.errorMessage)
XCTAssertFalse(sut.isLoading)
}
func testLoadTracksFailure() async {
mockRepo.shouldFail = true
await sut.loadTracks()
XCTAssertTrue(sut.tracks.isEmpty)
XCTAssertNotNil(sut.errorMessage)
}
}
final class MockTrackRepository: TrackRepositoryProtocol {
var stubbedTracks: [Track] = []
var shouldFail = false
func fetchAll() async throws -> [Track] {
if shouldFail { throw URLError(.notConnectedToInternet) }
return stubbedTracks
}
}
@testable import 访问 internal 符号;Mock 实现 Protocol,类似 Spring @MockBean。
UI 测试(XCUITest)
func testLessonListNavigation() {
let app = XCUIApplication()
app.launch()
XCTAssertTrue(app.navigationBars["课时"].exists)
app.tables.cells.firstMatch.tap()
XCTAssertTrue(app.staticTexts["详情"].waitForExistence(timeout: 2))
}
UI 测试慢且脆弱,覆盖关键用户路径即可;大部分逻辑应在单元测试验证。
调试工具
| 工具 | 用途 |
|---|---|
| LLDB 断点 | 运行时检查变量 |
| View Hierarchy | 查看 UI 层级与约束 |
| Instruments → Leaks | 内存泄漏 |