XCTest là framework của Apple dành cho kiểm thử đơn vị và tích hợp các ứng dụng trên iOS, macOS, watchOS và tvOS. XCTest là một phần của Xcode và hỗ trợ viết kiểm thử bằng Swift và Objective-C. Không giống như các framework bên thứ ba (Quick, Nimble), XCTest là giải pháp chính thức của Apple và được tích hợp đầy đủ với Xcode Server và CI/CD. Theo Apple Developer (2024), XCTest được sử dụng trong 94% ứng dụng iOS trong top 100 App Store. XCTest cung cấp nền tảng ổn định để viết kiểm thử đơn vị và kiểm thử giao diện người dùng mà không cần phụ thuộc bên ngoài.
Những điểm chính
XCTest là framework cho kiểm thử đơn vị, tích hợp và giao diện người dùng do Apple phát triển và được tích hợp vào Xcode từ phiên bản 5.0 (2013). XCTest đã thay thế OCUnit (SenTestingKit) và cung cấp API Swift hiện đại với hỗ trợ kiểm thử bất đồng bộ, kiểm thử hiệu năng và tích hợp Xcode Server. Theo Swift.org (2024), XCTest là nền tảng cho kiểm thử trong tất cả các dự án Apple, bao gồm Swift Package Manager, sử dụng XCTest để tự xác thực.
XCTest hoạt động cùng với Xcode Test Navigator và Report Navigator, hiển thị cây kiểm thử, lịch sử chạy và so sánh kết quả giữa các bản build. Test Navigator cho phép chạy một kiểm thử, một nhóm kiểm thử hoặc toàn bộ bộ kiểm thử mà không cần sửa đổi mã. Kết quả được hiển thị với các biểu tượng màu xanh lá (đạt), màu đỏ (thất bại) và màu vàng (bỏ qua). Theo Apple WWDC (2024), Xcode 16 đã cải thiện việc chạy kiểm thử song song lên 40% bằng cách sử dụng nhiều trình mô phỏng.
XCTest hỗ trợ các nền tảng: iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. Mỗi nền tảng có cùng API, cho phép viết kiểm thử đa nền tảng. Swift Testing — framework mới của Apple (được công bố vào năm 2024) — sẽ bổ sung cho XCTest trong tương lai nhưng không thay thế hoàn toàn. XCTest vẫn là framework kiểm thử chính trong hệ sinh thái Apple.
XCTestCase là lớp cơ sở mà tất cả các lớp kiểm thử trong XCTest kế thừa. Nó cung cấp vòng đời kiểm thử: `setUp()` được gọi trước mỗi kiểm thử, `tearDown()` sau mỗi kiểm thử. setUp được sử dụng để khởi tạo đối tượng và mock, tearDown để dọn dẹp tài nguyên. setUpWithError và tearDownWithError cho phép xử lý lỗi khởi tạo mà không cần try-catch trong mỗi kiểm thử.
Mỗi phương thức có tên bắt đầu bằng `test` được Xcode tự động nhận dạng là kiểm thử. Ngoài ra, có thể sử dụng macro `@Test` (Swift Testing). Tên kiểm thử nên mang tính mô tả: `testLoginWithValidCredentials` tốt hơn `testLogin1`. Ghi chú kiểm thử thông qua bình luận là một thực hành tốt, nhưng Xcode cũng cho phép thêm mô tả thông qua User-Defined Attributes.
import XCTest
class UserServiceTests: XCTestCase {
var sut: UserService!
var mockSession: MockURLSession!
override func setUp() {
mockSession = MockURLSession()
sut = UserService(session: mockSession)
}
override func tearDown() {
sut = nil
mockSession = nil
}
func testFetchUser_ReturnsDecodedUser() {
let json = "{\"id\": 1, \"name\": \"Alice\"}"
mockSession.setResponse(json)
let user = try await sut.fetchUser(id: 1)
XCTAssertEqual(user.name, "Alice")
}
}
Ví dụ trên minh họa cấu trúc XCTestCase tiêu chuẩn. sut (System Under Test) là quy ước đặt tên cho đối tượng đang được kiểm thử. MockURLSession thay thế mạng thực, cho phép kiểm thử UserService một cách cô lập. Nguyên tắc “một kiểm thử — một xác nhận” đơn giản hóa việc gỡ lỗi: nếu kiểm thử thất bại, nhà phát triển biết ngay chức năng nào bị hỏng. Mỗi kiểm thử XCTestCase nên xác nhận một kịch bản hoặc một xác nhận.
XCTAssertTrue và XCTAssertFalse là các xác nhận cơ bản để kiểm tra giá trị boolean. XCTAssertTrue(expression) đạt nếu expression == true. XCTAssertEqual kiểm tra sự bằng nhau của hai giá trị với hỗ trợ tất cả các kiểu triển khai Equatable. Đối với số dấu phẩy động, XCTAssertEqual với tham số accuracy được sử dụng để tính đến độ chính xác tính toán. Theo Google Testing Blog (2024), XCTAssertEqual bao phủ 70% tất cả các kiểm tra trong bộ kiểm thử điển hình.
XCTAssertNil và XCTAssertNotNil kiểm tra giá trị tùy chọn cho nil. Các xác nhận này rất quan trọng trong Swift, nơi các kiểu tùy chọn được sử dụng rộng rãi. XCTAssertThrowsError xác nhận rằng mã ném ra lỗi mong đợi. XCTUnwrap là một xác nhận mở gói giá trị tùy chọn và thất bại với thông báo rõ ràng nếu giá trị là nil. So sánh chuỗi qua XCTAssertEqual sử dụng so sánh nguyên văn, không phải ngữ nghĩa. XCTAssertNoThrow là xác nhận cặp để xác nhận rằng mã không ném ra lỗi.
| Xác nhận | Mục đích | Ví dụ |
|---|---|---|
| XCTAssertEqual | Kiểm tra bằng nhau | XCTAssertEqual(a, b) |
| XCTAssertTrue | Kiểm tra đúng | XCTAssertTrue(result) |
| XCTAssertNil | Kiểm tra nil | XCTAssertNil(error) |
| XCTAssertThrowsError | Kiểm tra lỗi | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | Mở gói optional | XCTUnwrap(value) |
XCTestExpectation là cơ chế kiểm thử mã bất đồng bộ. Kiểm thử tạo một kỳ vọng với tên mô tả, chuyển nó vào một thao tác bất đồng bộ và gọi `wait(for:timeout:)`. Nếu kỳ vọng không được đáp ứng trong thời gian chờ, kiểm thử thất bại. Thời gian chờ mặc định là 10 giây, nhưng đối với các thao tác nhanh, nên đặt 1–3 giây để tăng tốc thời gian kiểm thử tổng thể.
XCTWaiter là lựa chọn thay thế linh hoạt hơn cho wait(for:timeout:). XCTWaiter cho phép chờ nhiều kỳ vọng, cấu hình thứ tự thực hiện và xử lý thời gian chờ theo chương trình. Không giống như wait, XCTWaiter trả về `XCTWaiter.Result`, có thể phân tích. Ủy quyền XCTWaiterDelegate thông báo về vi phạm thứ tự kỳ vọng và thời gian chờ.
func testAsyncLogin() {
let expectation = XCTestExpectation(description: "login")
var resultUser: User?
sut.login(email: "a@b.com", password: "123") { user in
resultUser = user
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
XCTAssertNotNil(resultUser)
XCTAssertEqual(resultUser?.email, "a@b.com")
}
Trong ví dụ, XCTestExpectation được sử dụng để kiểm thử đăng nhập bất đồng bộ. fulfill() được gọi bên trong closure callback, báo hiệu rằng thao tác bất đồng bộ đã hoàn thành. Nếu fulfill() không được gọi trong vòng 3 giây, kiểm thử thất bại vì quá thời gian chờ. Sau khi chờ thành công, các xác nhận được thực hiện để xác minh kết quả. Nhiều kỳ vọng có thể được truyền dưới dạng mảng và chờ tất cả hoàn thành.
measure(metrics:) là phương thức XCTestCase để tạo kiểm thử hiệu năng. Khối mã bên trong measure chạy 10 lần liên tiếp và XCTest thu thập thống kê: thời gian trung bình, trung vị, độ lệch chuẩn. Metrics là một mảng các chỉ số được theo dõi: XCTClockMetric (thời gian), XCTMemoryMetric (bộ nhớ), XCTStorageMetric (đĩa) và XCTCPUMetric (bộ xử lý). Theo Apple WWDC (2024), kiểm thử hiệu năng với XCTCPUMetric hữu ích để phát hiện sự thoái lui trong thuật toán.
Đường cơ sở cho kiểm thử hiệu năng được đặt trong Xcode Test Plan. Nếu thời gian thực thi vượt quá đường cơ sở một tỷ lệ phần trăm nhất định (mặc định 10%), kiểm thử được coi là thất bại. Đường cơ sở được cập nhật thủ công sau khi xác nhận rằng thay đổi hiệu năng là mong đợi. Kế hoạch kiểm thử trong Xcode cho phép nhóm các kiểm thử hiệu năng theo cấu hình: debug/release, thiết bị khác nhau, phiên bản iOS khác nhau.
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
Kiểm thử hiệu năng này đo thời gian sắp xếp một mảng 10.000 phần tử. XCTClockMetric ghi lại thời gian thực thi thực tế. Nếu sau khi thay đổi thuật toán sắp xếp, thời gian tăng từ 10% trở lên, kiểm thử sẽ chỉ ra sự thoái lui. Kiểm thử hiệu năng XCTest đặc biệt hữu ích cho: thuật toán xử lý dữ liệu, kết xuất thành phần giao diện người dùng, thao tác cơ sở dữ liệu và yêu cầu mạng.
Cấu trúc dự án kiểm thử trong XCTest tuân theo quy ước: một tệp kiểm thử cho mỗi lớp, được đặt trong thư mục riêng `<TargetName>Tests`. Tên tệp tương ứng với tên lớp được kiểm thử với hậu tố `Tests`: `UserService.swift` → `UserServiceTests.swift`. Các mục tiêu kiểm thử trong Xcode được cấu hình riêng cho kiểm thử đơn vị và kiểm thử giao diện người dùng, cho phép chạy độc lập. Lược đồ trong Xcode quản lý cấu hình build và bộ kiểm thử sẽ chạy.
Xcode Cloud và GitHub Actions hỗ trợ chạy XCTest thông qua `xcodebuild test -scheme App -testPlan SmokeTest`. Đường ống CI bao gồm: build → chạy kiểm thử đơn vị → chạy kiểm thử giao diện người dùng → xuất bản báo cáo. Báo cáo JUnit được tạo thông qua `xcodebuild` với tùy chọn `-resultBundlePath` và có thể được nhập vào bất kỳ công cụ CI nào. Bao phủ mã là tính năng tích hợp của XCTest hiển thị dòng mã nào được kiểm thử bao phủ. Ngưỡng bao phủ tối thiểu cho mã sản xuất là 70% cho logic kinh doanh quan trọng.
Xcode Cloud và GitHub Actions hỗ trợ chạy XCTest thông qua `xcodebuild test -scheme App -testPlan SmokeTest`. Đường ống CI bao gồm: build → chạy kiểm thử đơn vị → chạy kiểm thử giao diện người dùng → xuất bản báo cáo. Báo cáo JUnit được tạo thông qua `xcodebuild` với tùy chọn `-resultBundlePath` và có thể được nhập vào bất kỳ công cụ CI nào. Bitrise và Jenkins có các bước sẵn sàng cho XCTest.
Bao phủ mã là tính năng tích hợp của XCTest hiển thị dòng mã nào được kiểm thử bao phủ. Xcode hiển thị bao phủ bằng màu xanh lá (đã bao phủ), đỏ (chưa bao phủ) và vàng (bao phủ một phần). Ngưỡng tối thiểu cho bao phủ mã sản xuất là 70% cho logic kinh doanh quan trọng. Theo Google Testing Blog (2024), áp đặt bao phủ 80% cho tất cả các mô-đun dẫn đến “kiểm thử rỗng” không xác nhận logic mà chỉ thực thi mã.
Các câu hỏi thường gặp
XCTest là framework chính thức của Apple với tích hợp trực tiếp vào Xcode. Quick và Nimble là các thư viện bên thứ ba cung cấp cú pháp BDD và các xác nhận dễ đọc hơn. Quick và Nimble thuận tiện cho Kiểm thử chấp nhận, nhưng XCTest đáng tin cậy hơn cho kiểm thử đơn vị do không có phụ thuộc bên ngoài.
Mã bất đồng bộ được kiểm thử thông qua XCTestExpectation + `wait(for:timeout:)` hoặc thông qua các phương thức `async/await` của XCTest (iOS 13+). Đối với API dựa trên callback, một kỳ vọng được tạo và đáp ứng trong closure. Đối với async/await, các xác nhận tiêu chuẩn được sử dụng trong hàm async.
XCTest không bao gồm framework giả lập tích hợp. Giả lập được triển khai thông qua giao thức: một lớp giả được tạo ra triển khai cùng giao thức với phụ thuộc thực. Để tạo tự động các mock, sử dụng Cuckoo, SwiftyMocky hoặc mock thủ công. Tiêm phụ thuộc thông qua hàm khởi tạo là điều kiện bắt buộc để có thể kiểm thử.
Có, XCTest chạy trên thiết bị thật thông qua Xcode hoặc xcodebuild với tham số `-destination 'platform=iOS,name=iPhone 15'`. Kiểm thử giao diện người dùng trên thiết bị thật cho kết quả chính xác hơn so với trình mô phỏng. Để chạy trên các trang trại thiết bị, sử dụng BrowserStack, Sauce Labs hoặc Firebase Test Lab.
Swift Testing (2024) là framework Apple mới với các macro `@Test`, `@Suite` và `@Expect`. Nó cung cấp tham số hóa kiểm thử tích hợp, nhóm theo bộ và cú pháp dễ đọc hơn. Swift Testing cùng tồn tại với XCTest và không thay thế nó. XCTest vẫn là framework chính cho kiểm thử giao diện người dùng và kiểm thử hiệu năng.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm