Swift单元测试中如何Mock AVFoundation的授权方法
问题描述
我使用静态函数AVCaptureDevice.authorizationStatus(for: .video)来检查相机权限。
在Objective-C中,使用OCMock很容易对该方法进行Mock,强制其返回.authorized枚举值。但OCMock并不支持Swift,请问该如何实现该需求?
补充说明
请不要关闭本问题,之前给出的「Swift单元测试Mock类静态方法」相关的重复问题参考方案无法解决该问题:那些方案仅适用于开发者自定义的类,不适用于系统API,无法生效。
解决方案
Swift 基于静态派发的特性,无法像OC那样直接通过runtime swizzle替换系统类静态方法的实现,目前行业内通用的处理方式是加一层协议抽象隔离系统API依赖,不需要借助第三方Mock框架就能实现测试控制,具体步骤如下:
- 定义相机权限检查的抽象协议,把业务需要用到的权限相关接口统一收拢
protocol CameraAuthorizationService { func currentVideoAuthStatus() -> AVAuthorizationStatus func requestVideoAccess(completion: @escaping (Bool) -> Void) }
- 实现默认的权限服务类,内部调用真实的系统AVFoundation API
struct SystemCameraAuthService: CameraAuthorizationService { func currentVideoAuthStatus() -> AVAuthorizationStatus { AVCaptureDevice.authorizationStatus(for: .video) } func requestVideoAccess(completion: @escaping (Bool) -> Void) { AVCaptureDevice.requestAccess(for: .video, completionHandler: completion) } }
- 业务代码中避免直接硬编码调用
AVCaptureDevice的静态方法,转而依赖上述协议,通过依赖注入传入具体实现,默认使用系统真实实现即可
class CameraUseViewController: UIViewController { private let authService: CameraAuthorizationService init(authService: CameraAuthorizationService = SystemCameraAuthService()) { self.authService = authService super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { self.authService = SystemCameraAuthService() super.init(coder: coder) } func whenViewDidLoadCheckAuth() { let status = authService.currentVideoAuthStatus() // 后续业务逻辑根据状态处理 } }
- 单元测试场景下,自定义Mock实现,自由控制返回的权限状态即可,完全不需要触碰系统API
class MockCameraAuthService: CameraAuthorizationService { var mockStatus: AVAuthorizationStatus = .notDetermined var mockRequestGranted: Bool = false func currentVideoAuthStatus() -> AVAuthorizationStatus { mockStatus } func requestVideoAccess(completion: @escaping (Bool) -> Void) { completion(mockRequestGranted) } } // 测试用例示例 func testAuthorizedStateCameraFlow() { let mockAuth = MockCameraAuthService() mockAuth.mockStatus = .authorized let vc = CameraUseViewController(authService: mockAuth) // 验证授权状态下的页面逻辑是否符合预期 }
如果项目存量代码太多,暂时没法全量重构为协议注入的形式,可以退而求其次使用XCTest UI测试能力,通过启动参数控制测试环境下的权限状态,但这种方式运行速度慢、稳定性差,仅适合作为临时方案。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

