Почему утверждение разрешенного UIColor не выполняется после изменения UIUserInterfaceStyle в модульном тесте?

Я работаю над проектом, в котором используются именованные цветовые активы, и я отвечаю за обновление модульных тестов, которые утверждают правильные значения цвета.

У нас есть подкласс XCTestCase (BaseXCTestCase), у которого есть свойство window, для которого установлено значение UIApplication.shared.firstKeyWindow:

var window = UIApplication.shared.firstKeyWindow!

firstKeyWindow определяется как windows.filter { $0.isKeyWindow }.first в расширении UIApplication:

var firstKeyWindow: UIWindow? {
    return windows.filter { $0.isKeyWindow }.first
}

Кроме того, есть два свойства, используемые для доступа к windowtraitCollection и userInterfaceStyle:

var traitCollection: UITraitCollection {
    return window.traitCollection
}

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

Класс, содержащий цветовые тесты, наследуется от 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)

}

Первые два утверждения теста успешны при утверждении цвета для светлого режима. Однако утверждения для цвета темного режима терпят неудачу, и сообщения об ошибках указывают, что цвет светлого режима и альфа-значения сохраняются.

Я устанавливаю точку останова в последнем утверждении светового режима, и печать 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, чего и ожидал.

Если я удалю код, который задает светлый стиль интерфейса и задает разрешенный цвет, то тест проходит:

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)

}

Тогда мой вопрос: почему утверждения для темного цвета и альфа-значений терпят неудачу? В частности, что заставляет цвет не разрешаться после переопределения window's overrideUserInterfaceStyle?

Разве следующий код не должен вызывать разрешение цвета с набором признаков, который содержит UserInterfaceStyle = Dark в качестве цвета темного режима и альфа-значений?

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

Если я поставлю логику темного режима выше логики светлого режима, то утверждения светлого режима не сработают. Я не знаю, что я не могу сделать между первыми и вторыми утверждениями, чтобы обеспечить соблюдение стиля пользовательского интерфейса.

Первоначальная мысль: это проблема основного потока? Не уверен, что модульные тесты гарантированно будут выполняться в основном потоке...

Josh Brown 16.05.2022 20:43

Несмотря на то, что на вопрос уже дан ответ, мне интересно, рассматривали ли вы просто создание экземпляров коллекций признаков с помощью инициализатора UITraitCollection(userInterfaceStyle:) вместо того, чтобы манипулировать коллекцией признаков окна. Это обеспечит лучшую изоляцию от окружающей среды для ваших тестов.

Vadim Belyaev 16.05.2022 21:27

@VadimBelyaev Я не думал об этом до сих пор. Я обновил наш набор тестов, чтобы создавать экземпляры коллекций черт через UITraitCollection(userInterfaceStyle:). Как вы сказали, я думаю, что это обеспечивает лучшую изоляцию от нашей тестовой среды.

Nick Kohrn 16.05.2022 22:33
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
3
3
18
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не думаю, что модульные тесты гарантированно будут работать в основном потоке, поэтому, возможно, попробуйте DispatchQueue.main.async для обновления пользовательского интерфейса и посмотрите, как это пойдет.

Я завернул утверждения в отправку в основную очередь, и это устранило сбои. Я думал, что обновления коллекции трейтов происходят в основном потоке, но я полагаю, что упустил из виду тот факт, что модульные тесты не гарантируют выполнение в основном потоке.

Nick Kohrn 16.05.2022 20:58

Другие вопросы по теме