swift 如何在单元测试期间模拟默认时区?

lo8azlld  于 2024-01-05  发布在  Swift
关注(0)|答案(3)|浏览(173)

在单元测试期间,我想临时模拟特定国家的默认时区。这就是我目前所做的,通过使用全局变量。

应用代码

import Foundation

var _TIME_ZONE_FOR_UNIT_TEST: TimeZone? = nil

extension TimeZone {
    static func current() -> TimeZone {
        if let _TIME_ZONE_FOR_UNIT_TEST = _TIME_ZONE_FOR_UNIT_TEST {
            return _TIME_ZONE_FOR_UNIT_TEST
        } else {
            return TimeZone.current
        }
    }
}

字符串

单元测试

func testToDayResolutionInCuba() throws {
    let cubaTimeZone = TimeZone(identifier: "America/Havana")!
    
    _TIME_ZONE_FOR_UNIT_TEST = cubaTimeZone
    defer {
        _TIME_ZONE_FOR_UNIT_TEST = nil
    }
    
    ...
    
    // ReminderUtils.toDayResolution is depending on TimeZone.current()
    let timestampWithoutTime = ReminderUtils.toDayResolution(timeMillis)


虽然它的工作,我不喜欢这样的方式,
1.使用全局变量

  1. if检查的额外运行时成本
    是否有更好的方法来执行特定时区的单元测试?
qlfbtfca

qlfbtfca1#

模拟可以通过改变NSTimeZone.default来完成

// Cuba will advance its clock by 1 hour, during 2021-03-14 00:00 am. This means during 2021-03-13 23:59 pm, after
// next 1 second, the time will be 2021-03-14 01:00 am.
func testToDayResolutionInCuba() throws {
    let cubaTimeZone = TimeZone(identifier: "America/Havana")!
    let oldDefault = NSTimeZone.default
    NSTimeZone.default = cubaTimeZone
    defer {
        NSTimeZone.default = oldDefault
    }
    
    var dateComponents = DateComponents()
    dateComponents.year = 2021
    dateComponents.month = 3
    dateComponents.day = 14
    dateComponents.hour = 0
    dateComponents.minute = 0
    dateComponents.second = 0
    
    let calendar = Calendar.current
    let date = calendar.date(from: dateComponents)!
    
    let timeMillis = date.timeMillis
    
    let timestampWithoutTime = ReminderUtils.toDayResolution(timeMillis)
    
    let dateWithoutTime = Date(timeIntervalSince1970: Double(timestampWithoutTime/1000))
    let result = calendar.dateComponents(in: cubaTimeZone, from: dateWithoutTime)
    
    XCTAssertEqual(2021, result.year)
    XCTAssertEqual(3, result.month)
    XCTAssertEqual(14, result.day)
    XCTAssertEqual(1, result.hour)
    XCTAssertEqual(0, result.minute)
    XCTAssertEqual(0, result.second)
}

字符串

lstz6jyr

lstz6jyr2#

**此解决方案在iOS 17上不起作用,因为Timezone.current不再到达NSTimeZone.systemTimeZone以进行搅拌。

TimeZoneNSTimeZone的直接 Package 器,TimeZone.current对应于NSTimeZone.systemTimeZone
因此,你可以用任何mocking框架来mock/stub NSTimeZone.systemTimeZone。这将帮助你从代码库中摆脱讨厌的if

mfpqipee

mfpqipee3#

正如在另一个答案https://stackoverflow.com/a/68979424中提到的。
自iOS 17以来,Timezone.current不再到达NSTimeZone.systemTimeZone,因此swizzing NSTimeZone不再工作。
但是有一种方法,新的TimeZone实现更喜欢TZ环境值。
达尔文的setenv1参数,以强制覆盖,如果任何设置之前,允许在运行时这样做,例如。

setenv("TZ", identifier, 1)

字符串
它本身并不工作,因为TimeZone内部使用缓存机制。但我们可以通过调用来欺骗无效过程:

CFTimeZoneResetSystem()


所有这些似乎在单元测试的情况下工作得很好:

func test() {
    setenv("TZ", "America/New_York", 1)
    CFTimeZoneResetSystem()

    print(TimeZone.current) // America/New_York (fixed)
    print(TimeZone.autoupdatingCurrent) // America/New_York (autoupdatingCurrent)
}

相关问题