GOAL: I want to UI test my SegmentedControl - check that each segment is selected when tapped on it
CONTEXT: The shoeType UISegmentedControl has 3 segments [City, Running, Baskets]
CODE: Here is my code:
ViewController.swift:
@IBOutlet weak var shoeType: UISegmentedControl!
// SegmentedControl pressed
@IBAction func shoeTypePressed(_ sender: UISegmentedControl) {
typeNumber = sender.selectedSegmentIndex // Assign the selected segment's index to typeNumber variable
if typeNumber == 0 {
type = "City"
} else if typeNumber == 1 {
type = "Running"
} else {
type = "Basket"
}
updateShoeImage() // Update the shoe image if type is pressed
updateOrderResult() // Update order result's message when a type is pressed
}
// Updates the shoe image's shown
func updateShoeImage() {
shoeSelection.image = UIImage(named: getShoeParams(type: type, gender: genderLbl.text ?? "", color: color))
}
// Sets the value of the text displayed on the UI regarding the parameters
func updateOrderResult() {
if name == "" || name == "Mr." || name == "Miss" {
if gender == "Boy" {
name = "Mr."
} else if gender == "Girl"{
name = "Miss"
}
}
orderResult.text = """
Hello \(name) I found this pair of \(type.lowercased()) shoes in \(color.lowercased()) size \(sizeLbl.text?.lowercased() ?? "")
"""
}
ViewControllerUITest.swift:
func testSegmentedControl_WhenTapped_ChangeSegment() {
app.launch()
let shoeTypeSegmentedControl = app.segmentedControls["city"]
app.segmentedControls["city"].tap()
XCTAssertEqual(shoeTypeSegmentedControl.label, "City")
}
There is so much going in this code that it is hard to test with its current design. But let's focus only on this one requirement: Tapping a shoe type shows the name of the type in lowercase somewhere in the order result.
I would not reach for a UI test to do this. UI tests are slow, fragile, and costly to maintain. The reason I want tests is to support refactoring, so I want the tests to be quick and stable.
Following my book iOS Unit Testing by Example, these are the steps a unit test needs:
To load the view controller (wherever it is in the storyboard), let's give it Storyboard ID. I like using the same name as the view controller.
On my machine, this test completes in 17ms. It's much faster than any UI test, and easily fast enough to support refactoring in small steps. (Remember, this is the reason I want tests.)
The assertion in the Arrange step is a precondition. Its job is to confirm that segment 1 is indeed "Running" which the rest of the test depends on.
If the order result doesn't contain what we expect, the assertion message reports the actual order result to make it easier to diagnose. I made the test fail on purpose to see how this message looks.
The test code itself is still hard to read. I would refactor it, moving some into setUp/tearDown, the precondition into its own test of the segment names, and some into helper methods:
This hides the details, expressing the intent of the test in a way that anyone can read.