I am working on a project that uses named color assets, and I am responsible for updating the unit tests that assert the correct color values.
We have an XCTestCase
subclass (BaseXCTestCase
) that has a window
property, which is set to a value of UIApplication.shared.firstKeyWindow
:
var window = UIApplication.shared.firstKeyWindow!
firstKeyWindow
is defined as windows.filter { $0.isKeyWindow }.first
in an UIApplication
extension:
var firstKeyWindow: UIWindow? {
return windows.filter { $0.isKeyWindow }.first
}
Additionally, there are two properties used for accessing window
‘s traitCollection
and userInterfaceStyle
:
var traitCollection: UITraitCollection {
return window.traitCollection
}
var userInterfaceStyle: UIUserInterfaceStyle {
get {
return window.traitCollection.userInterfaceStyle
}
set {
window.overrideUserInterfaceStyle = newValue
}
}
The class that contains the color tests inherits from BaseXCTestCase
. Then, our tests are configured as:
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)
}
The first two assertions of the test succeed when asserting the color for light mode. However, the assertions for the dark mode color fail, and the failure messages indicate that the light mode color and alpha values are persisting.
I set a breakpoint at the final light-mode assertion, and printing traitCollection
provides me with the following output, which I expect:
<UITraitCollection: 0x60000198a760;
UserInterfaceIdiom = Phone,
DisplayScale = 3,
DisplayGamut = P3,
HorizontalSizeClass = Compact,
VerticalSizeClass = Regular,
UserInterfaceStyle = Light,
UserInterfaceLayoutDirection = LTR,
ForceTouchCapability = Unavailable,
PreferredContentSizeCategory = L,
AccessibilityContrast = Normal,
UserInterfaceLevel = Base
>
In the printed output, I see UserInterfaceStyle = Light
.
Then, I set a breakpoint at the final dark-mode assertion, and printing traitCollection
provides me with the following output, which I expect:
<UITraitCollection: 0x600001990460;
UserInterfaceIdiom = Phone,
DisplayScale = 3,
DisplayGamut = P3,
HorizontalSizeClass = Compact,
VerticalSizeClass = Regular,
UserInterfaceStyle = Dark,
UserInterfaceLayoutDirection = LTR,
ForceTouchCapability = Unavailable,
PreferredContentSizeCategory = L,
AccessibilityContrast = Normal,
UserInterfaceLevel = Base
>
This time, I see UserInterfaceStyle = Dark
, which I expect.
If I remove the code that sets the interface style to light and sets the resolved color, then the test passes:
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)
}
My question is then, why are the assertions for the dark-mode color and alpha values failing? Specifically, what is causing the color to not be resolved after overriding window
‘s overrideUserInterfaceStyle
?
Shouldn’t the following code cause the color to be resolved with the trait collection that contains UserInterfaceStyle = Dark
as the dark-mode color and alpha values?
userInterfaceStyle = .dark
subject = subject.resolvedColor(with: traitCollection)
If I move the dark-mode logic above the light-mode logic, then the light-mode assertions fail. I don’t know what I am failing to do between the first assertions and second assertions to ensure the user interface style is being respected.
>Solution :
I don’t think unit tests are guaranteed to run on the main thread, so maybe try a DispatchQueue.main.async
for the UI update and see how that goes.