You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何在XCUITest场景下Swizzle Date(非NSDate)类型?

XCUITest运行时修改Date返回值实现方案

直接在XCUITest测试target里Swizzle Date的方法无法生效,因为XCUITest测试进程和App主进程是完全隔离的,测试代码里的方法交换不会作用到App运行逻辑中,可以通过以下两种方案实现需求:

方案1:注入可配置的时间提供者(推荐,稳定性最高)

这个方案不需要做危险的系统方法交换,对业务代码侵入极低,也不会影响线上逻辑:

  • 首先在主App target中给Date扩展可覆写的时间获取入口,默认实现和系统Date()行为完全一致:
extension Date {
    /// 时间获取提供者,生产环境默认返回系统当前时间
    static var currentProvider: () -> Date = { Date() }
    
    /// 业务代码统一用这个方法获取当前时间
    static func now() -> Date {
        currentProvider()
    }
}

把业务代码中所有直接写let date = Date()的位置替换为let date = Date.now(),线上运行时返回值和原有逻辑完全一致,没有任何副作用。

  • 在XCUITest测试代码启动App时,通过启动参数传入需要的时间偏移量,比如需要加2小时就传7200秒:
let app = XCUIApplication()
// 传入时间偏移量,单位秒
app.launchArguments += ["-UITestTimeOffset", "7200"]
app.launch()
  • 在主App的启动入口(AppDelegate或SwiftUI App的初始化方法)中,检测到UITest启动参数时替换时间提供者:
// 检测UI测试环境配置
if let offsetValue = UserDefaults.standard.string(forKey: "UITestTimeOffset"),
   let timeOffset = TimeInterval(offsetValue) {
    Date.currentProvider = {
        Date().addingTimeInterval(timeOffset)
    }
}

配置完成后,运行UI测试时所有调用Date.now()的位置都会自动返回偏移后的时间,示例中系统时间为2022/07/06 22:31时,拿到的时间就会是加2小时后的自定义时间,对应label显示符合预期。

方案2:Runtime Swizzle Date初始化方法(无业务代码侵入)

如果不想改动现有业务代码,可以在主App内通过方法交换修改Date的返回值,注意Swizzle逻辑必须写在主App target内,不能写在测试target:

  • 同样通过上述启动参数的方式判断当前是否处于UI测试环境、需要偏移多少时间
  • App启动时检测到测试参数就执行方法交换,代码示例:
import ObjectiveC.runtime

// 先定义全局变量存储测试用时间偏移量
private var testTimeOffset: TimeInterval = 0

// 仅在UI测试环境下执行swizzle
if let offsetValue = UserDefaults.standard.string(forKey: "UITestTimeOffset"),
   let timeOffset = TimeInterval(offsetValue) {
    testTimeOffset = timeOffset
    guard let originalMethod = class_getInstanceMethod(
        Date.self,
        #selector(Date.init)
    ), let swizzledMethod = class_getInstanceMethod(
        Date.self,
        #selector(Date.swizzled_init)
    ) else { return }
    method_exchangeImplementations(originalMethod, swizzledMethod)
}

extension Date {
    @objc func swizzled_init() -> Date {
        // 调用原实现获取真实系统时间,再加上自定义偏移
        let realDate = self.swizzled_init()
        return realDate.addingTimeInterval(testTimeOffset)
    }
}

注意:Swizzle系统基础类的初始化方法存在不可控风险,可能影响App内部系统API依赖时间的逻辑,非必要不优先使用这个方案。

内容的提问来源于stack exchange,提问作者PJR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:24:29