UISwitch Unit test with sendActions(for: .valueChanged)

356 Views Asked by At

GOAL: I'm currently unit testing the behavior of my UISwitch when it is pressed.

ISSUE: My issue I that the sendActions(for: .valueChanged) that I use to programmatically change the UISwitch's state is not working as I imagined. It doesn't make it change from false to true (in my example).

WHAT DID I DO ?: I'm using the following path:

  1. I declared my UISwitch as true
  2. I used sendActions(for: .valueChanged) for changing it to false
  3. I finally used XCTAssertEqual() to check if it correctly equal to false as expected. (I also could have used XCTAssertFalse() to check directly if it was false.

Ps: I correctly passed loadViewIfNeeded() in my setUPWithError()

QUESTION: How can I test my UISwitch ??

CODE: Here is my code:

    var sut: ViewController!
    
    override func setUpWithError() throws {
        super.setUp()
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        sut = storyboard.instantiateViewController(withIdentifier: "ViewController") as? ViewController
        sut.loadViewIfNeeded()
    }

    func testGenderButton_ShouldUpdateValueWhenPressed() {
        sut.genderSwitch.isOn = true
        sut.genderSwitch.sendActions(for: .valueChanged)
        XCTAssertEqual(sut.genderSwitch.isOn, false, "\(sut.genderSwitch.isOn) should be equal to false")
    }
2

There are 2 best solutions below

4
matt On BEST ANSWER

Don't test it. A UISwitch cannot be a subject-under-test! Unit tests are for logic, not interface, and besides, UISwitch isn't your code — it's Apple's class and you already know how it behaves.

1
Jon Reid On

I interpret the question not as literal code to test, but "Why isn't it changing the state?" While we shouldn't test Apple's code, we definitely should unit test our code — including our UI code.

I thought I could pull a trick from my book by pumping the run loop, hoping this would give UIKit a chance to process the event. But this didn't change anything.

Here's what Rachel Brindle writes in Unit Testing UIKit - Views:

fooCell.toggle.isOn = false
fooCell.toggle.sendActions(for: .valueChanged) // 5.1

5.1 This is part of the step of modeling what happens when the user toggles a switch - first I toggle the isOn property, then I tell the switch to send the actions for the valueChanged UIControl.Event - as per the UISwitch documentation. You’ll note that I do this instead of, say, calling the onToggle property of the given cell. This is because the particular mechanism used to notify the view controller/model that the UI changed is an implementation detail and is tested by using the UISwitch. In other words, by using UISwitch.sendActions(for:), I’m indirectly asserting the onToggle mechanism, in addition to asserting that the UISwitch on the ToggleTableViewCell was correctly set up. This also has the additional benefit of making it easier to change how the toggle switch -> update SettingsManager in the future - I don’t need to update the tests and, done right, it’s a green-to-green refactor.

So the correct way to unit test your UISwitch handling is:

  1. Set its value to the desired value to test
  2. Call sendActions(for:) to trigger your code, which responds to the new value.

In other words, this is your chance not to test that Apple changes the value, but to test how your code handles the new value.