Trong quy trình phát triển iOS truyền thống, việc thực thi các bài kiểm thử tự động (XCTest) thường phụ thuộc hoàn toàn vào môi trường Xcode và công cụ dòng lệnh xcodebuild. Điều này tạo ra rào cản lớn đối với các hệ thống tích hợp liên tục (CI) chạy trên nền tảng Linux hoặc các máy trạm không cài đặt IDE đầy đủ. Công cụ tidevice đã giải quyết vấn đề này bằng cách thiết lập một kênh giao tiếp trực tiếp với thiết bị iPhone, cho phép khởi chạy và điều khiển XCTest mà không cần sự can thiệp của Xcode.
Cơ chế hoạt động cốt lõi
Để hiểu rõ cách tidevice vận hành, chúng ta cần phân tích hai thành phần kỹ thuật quan trọng: XCTestConfiguration và XCTestManager.
1. Tạo lập XCTestConfiguration
Trước khi bài kiểm thử được chạy, một đối tượng cấu hình cần được gửi xuống thiết bị. Trong Xcode, việc này được thực hiện tự động, nhưng tidevice phải tự xây dựng gói cấu hình này. Cụ thể, tidevice sẽ đóng gói các thông số như Bundle ID của ứng dụng cần test, các bộ test cụ thể cần chạy, và các tham số môi trường vào một đối tượng XCTestConfiguration được serialization dưới dạng binary plist (bplist).
Dưới đây là ví dụ về cách logic tạo cấu hình có thể được triển khai:
def build_test_config(app_bundle_id, target_app_id, session_id):
"""
Tạo đối tượng cấu hình XCTest để gửi tới thiết bị.
"""
config_data = {
'testBundleURL': app_bundle_id,
'targetApplicationBundleID': target_app_id,
'sessionIdentifier': session_id,
'reportResults': True
}
# Chuyển đổi dictionary thành đối tượng bplist
return BPlDictSerializer.serialize(config_data)
2. Giao tiếp với XCTestManager
Sau khi có cấu hình, tidevice cần một cơ chế để nói chuyện với daemon kiểm thử đang chạy trên iOS. Nó sử dụng giao thức DTX (Debugging Transmission Exchange) để thiết lập proxy kết nối tới interface của XCTestManager.
Điểm mấu chốt trong kết nối này là việc sử dụng đường truyền DTX proxy với định danh cụ thể, cho phép tidevice giả lập vai trò của Xcode:
class DeviceConnection:
def setup_test_channel(self):
# Thiết lập kênh giao tiếp với daemon XCTest trên thiết bị
channel_name = "dtxproxy:XCTestManager_IDEInterface:XCTestManager_DaemonConnectionInterface"
return self._dtx_channel.connect(channel_name)
Quy trình thực thi kiểm thử
Luồng hoạt động của tidevice khi chạy XCTest diễn ra theo 5 bước chính:
- Xác lập kết nối: Kiểm tra kết nối USB với thiết bị iOS và xác minh tính sẵn sàng của ứng dụng Runner (ví dụ: WebDriverAgentRunner).
- Dữ liệu cấu hình: Xử lý các tham số dòng lệnh để sinh ra payload
XCTestConfiguration. - Khởi tạo phiên: Gửi tín hiệu khởi động process runner trên thiết bị thông qua giao thức lockdown/service.
- Điều khiển thực thi: Gửi gói tin cấu hình qua kênh DTX đã thiết lập để kích hoạt các method test.
- Theo dõi kết quả: Lắng nghe luồng log (syslog) từ thiết bị, phân tích và hiển thị trạng thái Pass/Fail ra màn hình người dùng.
Ví dụ thực tế
Giả sử chúng ta có ứng dụng cần kiểm thử là com.example.app và gói Runner là com.example.app UITests. Thay vì sử dụng xcodebuild, ta có thể sử dụng tidevice như sau:
tidevice xcuitest --bundle-id com.example.UITests.runner --target-bundle-id com.example.app
Khi lệnh được thực thi, hệ thống sẽ trả về luồng log chi tiết theo thời gian thực:
[Info] Connection established with device.
[Log] Test Suite 'UITests.xctest' started at 2023-10-01 10:00:00.
[Log] Test Case '-[UITests testLoginFunction]' started.
[Log] t = 0.00s Launch com.example.app
[Log] t = 0.15s Wait for application to idle
[Log] Test Case '-[UITests testLoginFunction]' passed (1.200 seconds).
Nhờ cơ chế này, các kỹ sư kiểm thử có thể dễ dàng tích hợp bài test UI vào các pipeline tự động hóa mà không bị ràng buộc bởi hạ tầng phức tạp của Apple.