swiftxcodeswiftuixcode-ui-testing

SwiftUI toggle not being toggled in UI test


I can't get UI tests to toggle a Toggle in a SwiftUI Form. It seems that app.switches[*name*].tap() does nothing. Does anyone else have experienced this? Ideas?

The code below is a demonstration of the issue. It's a simple form with four toggles that generates a int value based on the toggles positions.

I have the following SwiftUI view:

struct ContentView: View {
    
    @State var sw1: Bool = false
    @State var sw2: Bool = false
    @State var sw3: Bool = false
    @State var sw4: Bool = false
    
    
    var valueString: String {
        var ret: Int = 0
        if sw1 {
            ret = ret + 1
        }
        if sw2 {
            ret = ret + 2
        }
        if sw3 {
            ret = ret + 4
        }
        if sw4 {
            ret = ret + 8
        }
        return String(ret)
    }
    
    var body: some View {
        NavigationStack {
            Form {
                Section("Binary switches"){
                    Toggle("1", isOn: $sw1)
                        .accessibilityIdentifier("sw1")
                    Toggle("2", isOn: $sw2)
                        .accessibilityIdentifier("sw2")
                    Toggle("4", isOn: $sw3)
                        .accessibilityIdentifier("sw3")
                    Toggle("8", isOn: $sw4)
                        .accessibilityIdentifier("sw4")
                }
                Section("Value") {
                    Text(valueString)
                        .accessibilityIdentifier("value")
                        .accessibilityValue(valueString)
                }
            }
            .navigationTitle("Test Form")
        }
    }
}

And the following UI tests Code:

func testRandomSwitches() throws {
    let app = XCUIApplication()
    app.launch()
    
    // Get the binary switches
    let sw1 = app.switches["sw1"]
    let sw2 = app.switches["sw2"]
    let sw3 = app.switches["sw3"]
    let sw4 = app.switches["sw4"]
    
    // randomly switch on some of the switches
    let randomOnes = [sw1, sw2, sw3, sw4].map { _ in Bool.random() }
    if randomOnes[0] { sw1.tap() }
    if randomOnes[1] { sw2.tap() }
    if randomOnes[2] { sw3.tap() }
    if randomOnes[3] { sw4.tap() }
    
    // calculate the expected value
    let expectedValue = randomOnes.enumerated()
        .filter { $0.element }
        .map { 1 << $0.offset }
        .reduce(0, +)
    
    // get the value text and assert that it matches the expected value
    let value = app.staticTexts["value"].value as? String ?? ""
    XCTAssertEqual(value, String(expectedValue))
}

When I run this test, the switches never get toggled, and the value string always reads "0". The UITest code fails on XCTAssertEqual(value, String(expectedValue)). I'm not sure what I'm doing wrong.

If I try to record the toggle tap I always get the error "Timestamped Event Matching Error: Failed to find matching element".

Can anyone help me figure out why the switches aren't getting toggled in my UI tests?


Solution

  • Figured it out. It seems that I need to access the first switch in the switch. Really weird. But I gess UI testing is weird. It would be much appreciated if anyone got a less hacky solution.

    Non working code:

    sw1.tap()
    

    Working code:

    sw1.switches.firstMatch.tap()