Search code examples
swiftcocoa-touchxctestxctestcase

UITextFieldDelegate not called in XCTestCase


I'm just starting to get familiar with writing tests using XCTestCase and am exploring its capabilities. I have a simple (if contrived) app that loads a screen with a button and a textfield. When the textfield is selected the view controller populates the textfield:

public func textFieldDidBeginEditing(_ textField: UITextField) {
    textField.text = "abcd"
}

I also have functionality that selects the field when the button is pressed:

@IBAction public func selectField(_ sender: Any?) {
    print("button press")
    textfield.becomeFirstResponder()
}

The code in my test case, programatically taps the button using vc.button.sendActions(for: .touchUpInside)

However, it doesn't appear that the textfield ever becomes first responder and thus the delegate methods are not called. Below is the full test case class:

var vc: ViewController!

override func setUp() {
    let sb = UIStoryboard(name: "Main", bundle: nil)
    vc = sb.instantiateInitialViewController() as? ViewController
    _ = vc.view
}

override func tearDown() {

}

func test_textfieldPopulatesOnSelection() {

    vc.button.sendActions(for: .touchUpInside)

    let expectation = XCTestExpectation(description: "waiting for textfield to become first responder")
    DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
        print("textfield.text: \(self.vc.textfield.text ?? "")")
        expectation.fulfill()
    }

    wait(for: [expectation], timeout: 13.0)
}

While I realize that I'm probably conflating the concepts of UI testing and unit testing, I'm still curious why my delegate methods are not being called.


Solution

  • I have a simple (if contrived) app that loads a screen with a button and a textfield

    No, you don't — at least not in any meaningful sense of the notion "loads a screen". You have a view controller and its view, created in setup, but you have not done anything with them. The view controller is not part of the app's view controller hierarchy, and its view is not in the interface, and so the text field that it contains is not in the interface. A text field not in the interface isn't going to do anything. It cannot be first responder, as there is nothing for it to respond to. The first responder is a feature of the app's window, and your text field is not in any window.

    In any case, your use of a unit test here is misconceived. It isn't so much that you are conflating unit tests with user interface tests; it's that you're not honing in on what testing is. There is no point testing the concept that saying becomeFirstResponder makes a text field first responder; you know that that is true, so there is nothing to test. Your job in a unit test is to test your app's functionality, not to test the Cocoa framework.