UI Tests - isSelected is always returning false
Asked Answered
G

5

11

We have updated out Swift 2.3 project to Swift 3 recently using Xcode 8.2.1 (8C1002), and now most of our UI Tests related with tableViews and the isSelected property aren't working. It's always returning false, even when the object is selected (we can see it in the iOS Simulator).

Has anyone experienced similar issues? Our code used to work normally in Swift 2.3 before the conversion. Here is how we retrieve a tableView cell:

let cell = app.tables.cells.element(at: 4)

Note: app is a XCUIApplication.

And here is how we check if it's selected or not:

XCTAssert(cell.isSelected)

Another observation is that we are sure that the object exists because waitForExpectations is returning true:

let existsPredicate = NSPredicate(format: "exists = 1")
expectation(for: existsPredicate, evaluatedWith: cell, handler: nil)
waitForExpectations(timeout: 20, handler: nil)

EDIT: In order to replace isSelected, I've tried to use NSPredicate with selected = 1 and with isSelected = 1. None worked. I also tried to use acessibilityValue based in other question's answer, however it wasn't that simple since sometimes the items in my table view are selected/unselected programatically. Also, that method involved adding test code to the app, which isn't a good practice.

EDIT AFTER BOUNTY END: Since no one could find a solution for that problem and that's obviously a bug in Xcode, I've submitted a bug report to Apple. I will comment here when they release an Xcode version with the fix.

EXTRA EDIT: One day after my last edit, dzoanb came with a functional answer.

Glossectomy answered 21/3, 2017 at 18:48 Comment(0)
P
10

I made a few tests and a little research. You can check out the app created for this purpose >>here<<. It would be great if you could check it out (it required a little bit of work). There are also UI tests to prove it works. Also, two options are available, one is vanilla XCTest and one library with a lot of helpers I'm creating with my colleagues AutoMate. But that's not the point.

Here is what I found out:

1) isSelected property of XCUIElement depends on accessibilityTrait. Element to be selected in XCTest has to have UIAccessibilityTraitSelected set.

2) I couldn't reproduce Your problem but I was able to control isSelected property.

3) Yes, it requires a little bit of code, but should work well with VoiceOver if it is important for You.

All necessary code is in Your custom UITableViewCell subclass. And uses overriding UIAccessibilityElement accessibilityTraits property.

private var traits: UIAccessibilityTraits = UIAccessibilityTraitNone

// MARK: UITableViewCell life cycle
override func awakeFromNib() {
    super.awakeFromNib()
    traits = super.accessibilityTraits
}

// MARK: UIAccessibilityElement
override var accessibilityTraits: UIAccessibilityTraits {
    get {
        if isSelected {
            return traits | UIAccessibilityTraitSelected
        }
        return traits
    }

    set {
        traits = newValue
    }
}

Hope it helps.

Psittacine answered 7/4, 2017 at 9:14 Comment(0)
S
1

Couldn't get that code to compile under Swift 4. This worked for me.

public override var accessibilityTraits: UIAccessibilityTraits {
    get {
        if isSelected {
            return super.accessibilityTraits.union(.selected)
        }
        return super.accessibilityTraits
    }

    set {
        super.accessibilityTraits = newValue
    }
}
Shanks answered 5/3, 2019 at 18:7 Comment(0)
G
0

Have you tried making a break point before and after the tap, and check the value of the cell? Like the WWDC video here: https://youtu.be/7zMGf-0OnoU (See from 10 minutes in)

Gaff answered 21/3, 2017 at 19:53 Comment(7)
It didn't work for me. Instead of the result I received "Message from debugger: The LLDB RPC server has crashed. The crash log is located in ~/Library/Logs/DiagnosticReports and has a prefix 'lldb-rpc-server'. Please file a bug and attach the most recent crash log.".Glossectomy
I used prints instead, and the value of isSelected was false before and after the tap, and it was selected in the simulator after that tap.Glossectomy
I tried to print cell's value as well instead of isSelected ( print(cell.value!) ). Nothing came out.Glossectomy
Did you try making sure that Xcode sees the target as hittable? Try the 'isHittable' property of XCUIElement, and try to inspect the cell with the Accesibility Inspector. You could also try to record the test, to see if Xcode would have done anything differently with the upgrade to Swift 3.0?Gaff
Yes, it's hittable. Just tested it with an assert. How can I use the record feature to create an assert with Xcode? The cell pressing is working, I can see it in the simulator, but still cell.isSelected returns false.Glossectomy
You don't create asserts with the record feature. You record a scenario, and then you add asserts afterwards. So if you record the whole scenario, you should be able to add asserts right before the cell is tapped and right after.Gaff
That's exactly what I meant... I already have the scenario, and I already have the tap. The tap already works, I can see it working in the iOS Simulator, but cell.isSelected still returns false. Since I can't record asserts and the scenario is ready, the record feature is useless in my case.Glossectomy
C
0

isSelected only works on views which inherit from UIControl. UIControl.isSelected informs XCUIElement.isSelected.

Since UITableViewCell does not inherit from UIControl, you aren't seeing the value you want in your tests when you observe cell.isSelected.

I suggest that if you want this to be testable via UI tests that you file a feature request with Apple to make UIControl a protocol, which you could then extend your cells to conform to, or add UITableViewCell.isSelected to the properties that inform XCUIElement.isSelected.

Copyholder answered 22/3, 2017 at 11:43 Comment(1)
Also, UITableViewCell has its own isSelected property. developer.apple.com/reference/uikit/uitableviewcell/…Glossectomy
B
0

@dzoanb solution can work without adding a private var:

override var accessibilityTraits: UIAccessibilityTraits {
    get {
        if isSelected {
            return super.accessibilityTraits | UIAccessibilityTraitSelected
        }
        return super.accessibilityTraits
    }

    set {
        super.accessibilityTraits = newValue
    }
}
Batista answered 26/5, 2018 at 20:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.