Я работаю над проектом, в котором используются именованные цветовые активы, и я отвечаю за обновление модульных тестов, которые утверждают правильные значения цвета.
У нас есть подкласс 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
}
Кроме того, есть два свойства, используемые для доступа к window
traitCollection
и 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)
Если я поставлю логику темного режима выше логики светлого режима, то утверждения светлого режима не сработают. Я не знаю, что я не могу сделать между первыми и вторыми утверждениями, чтобы обеспечить соблюдение стиля пользовательского интерфейса.
Несмотря на то, что на вопрос уже дан ответ, мне интересно, рассматривали ли вы просто создание экземпляров коллекций признаков с помощью инициализатора UITraitCollection(userInterfaceStyle:)
вместо того, чтобы манипулировать коллекцией признаков окна. Это обеспечит лучшую изоляцию от окружающей среды для ваших тестов.
@VadimBelyaev Я не думал об этом до сих пор. Я обновил наш набор тестов, чтобы создавать экземпляры коллекций черт через UITraitCollection(userInterfaceStyle:)
. Как вы сказали, я думаю, что это обеспечивает лучшую изоляцию от нашей тестовой среды.
Я не думаю, что модульные тесты гарантированно будут работать в основном потоке, поэтому, возможно, попробуйте DispatchQueue.main.async
для обновления пользовательского интерфейса и посмотрите, как это пойдет.
Я завернул утверждения в отправку в основную очередь, и это устранило сбои. Я думал, что обновления коллекции трейтов происходят в основном потоке, но я полагаю, что упустил из виду тот факт, что модульные тесты не гарантируют выполнение в основном потоке.
Первоначальная мысль: это проблема основного потока? Не уверен, что модульные тесты гарантированно будут выполняться в основном потоке...