为什么在单元测试中更改 UIUserInterfaceStyle 后断言已解决的 UIColor 失败?

Why Is Asserting Resolved UIColor Failing After Changing UIUserInterfaceStyle in Unit Test?

我正在开发一个使用命名颜色资产的项目,我负责更新断言正确颜色值的单元测试。

我们有一个 XCTestCase subclass (BaseXCTestCase),它有一个 window 属性,其值设置为 UIApplication.shared.firstKeyWindow:

var window = UIApplication.shared.firstKeyWindow!

firstKeyWindowUIApplication 扩展中定义为 windows.filter { [=23=].isKeyWindow }.first

var firstKeyWindow: UIWindow? {
    return windows.filter { [=11=].isKeyWindow }.first
}

此外,还有两个属性用于访问 windowtraitCollectionuserInterfaceStyle

var traitCollection: UITraitCollection {
    return window.traitCollection
}

var userInterfaceStyle: UIUserInterfaceStyle {
    get {
        return window.traitCollection.userInterfaceStyle
    }
    set {
        window.overrideUserInterfaceStyle = newValue
    }
}

包含颜色测试的class继承自BaseXCTestCase。然后,我们的测试配置为:

func testColor() {
  var subject = .namedColor

  /// Light mode
  userInterfaceStyle = .light
  subject = subject.resolvedColor(with: traitCollection)
  XCTAssertEqual(subject.hexString(), "#ABC123")
  XCTAssertEqual(subject.alpha, 1.0)

  /// Dark mode
  userInterfaceStyle = .dark
  subject = subject.resolvedColor(with: traitCollection)
  XCTAssertEqual(subject.hexString(), "#BCD234")
  XCTAssertEqual(subject.alpha, 1.0)

}

当断言灯光模式的颜色时,测试的前两个断言成功。但是,暗模式颜色的断言失败,失败消息表明亮模式颜色和 alpha 值持续存在。

我在最后的 light-mode 断言处设置了一个断点,打印 traitCollection 为我提供了以下输出,这是我所期望的:

<UITraitCollection: 0x60000198a760; 
  UserInterfaceIdiom = Phone, 
  DisplayScale = 3, 
  DisplayGamut = P3, 
  HorizontalSizeClass = Compact, 
  VerticalSizeClass = Regular,
  UserInterfaceStyle = Light, 
  UserInterfaceLayoutDirection = LTR, 
  ForceTouchCapability = Unavailable,
  PreferredContentSizeCategory = L, 
  AccessibilityContrast = Normal, 
  UserInterfaceLevel = Base
>

在打印输出中,我看到 UserInterfaceStyle = Light

然后,我在最后的暗模式断言处设置了一个断点,打印 traitCollection 为我提供了以下输出,这是我期望的:

<UITraitCollection: 0x600001990460; 
  UserInterfaceIdiom = Phone, 
  DisplayScale = 3, 
  DisplayGamut = P3, 
  HorizontalSizeClass = Compact, 
  VerticalSizeClass = Regular, 
  UserInterfaceStyle = Dark, 
  UserInterfaceLayoutDirection = LTR, 
  ForceTouchCapability = Unavailable, 
  PreferredContentSizeCategory = L, 
  AccessibilityContrast = Normal, 
  UserInterfaceLevel = Base
>

这一次,我看到了 UserInterfaceStyle = Dark,这是我所期望的。

如果我去掉设置界面样式为light和设置resolved color的代码,那么测试通过:

func testColor() {
  var subject = .namedColor

  /// Light mode
  XCTAssertEqual(subject.hexString(), "#ABC123")
  XCTAssertEqual(subject.alpha, 1.0)

  /// Dark mode
  userInterfaceStyle = .dark
  subject = subject.resolvedColor(with: traitCollection)
  XCTAssertEqual(subject.hexString(), "#BCD234")
  XCTAssertEqual(subject.alpha, 1.0)

}

那么我的问题是,为什么暗模式颜色和 alpha 值的断言失败了?具体来说,是什么导致覆盖windowoverrideUserInterfaceStyle后颜色无法解析?

下面的代码不应该使用包含 UserInterfaceStyle = Dark 作为暗模式颜色和 alpha 值的特征集合来解析颜色吗?

userInterfaceStyle = .dark
subject = subject.resolvedColor(with: traitCollection)

如果我将暗模式逻辑移动到亮模式逻辑之上,那么亮模式断言就会失败。我不知道在第一个断言和第二个断言之间我没有做什么以确保尊重用户界面样式。

我认为单元测试不能保证在主线程上 运行,所以也许可以尝试 DispatchQueue.main.async 更新 UI,看看结果如何。