Test the Text Field Delegate Method

We’ve finished testing the long method for the Submit button action. Here’s the last method we want to test. It determines what happens when you press the Return key from within each text field.

Refactoring/Refactoring/ChangePasswordViewController.swift
 func​ ​textFieldShouldReturn​(_ textField: ​UITextField​) -> ​Bool​ {
 if​ textField === oldPasswordTextField {
  newPasswordTextField.​becomeFirstResponder​()
  } ​else​ ​if​ textField === newPasswordTextField {
  confirmPasswordTextField.​becomeFirstResponder​()
  } ​else​ ​if​ textField === confirmPasswordTextField {
 changePassword​()
  }
 return​ ​true
 }

We’ll follow the approach laid out in Test Delegate Methods. As a first test, let’s make sure each text field has its delegate hooked up:

Refactoring/RefactoringTests/ChangePasswordViewControllerTests.swift
 func​ ​test_textFieldDelegates_shouldBeConnected​() {
 XCTAssertNotNil​(sut.oldPasswordTextField.delegate,
 "oldPasswordTextField"​)
 XCTAssertNotNil​(sut.newPasswordTextField.delegate,
 "updatedPasswordTextField"​)
 XCTAssertNotNil​(sut.confirmPasswordTextField.delegate,
 "confirmPasswordTextField"​)
 }

To call the method through the text field delegate, add a function to TestHelpers.swift:

Refactoring/RefactoringTests/TestHelpers.swift
 @discardableResult​ ​func​ ​shouldReturn​(​in​ textField: ​UITextField​) -> ​Bool​? {
  textField.delegate?.​textFieldShouldReturn​?(textField)
 }

Now we can test that hitting Return in the first two fields moves the input focus to the next field:

Refactoring/RefactoringTests/ChangePasswordViewControllerTests.swift
 func​ ​test_hittingReturnFromOldPassword_shouldPutFocusOnNewPassword​() {
 putInViewHierarchy​(sut)
 
 shouldReturn​(in: sut.oldPasswordTextField)
 
 XCTAssertTrue​(sut.newPasswordTextField.isFirstResponder)
 }
 
 func​ ​test_hittingReturnFromNewPassword_shouldPutFocusOnConfirmPassword​() {
 putInViewHierarchy​(sut)
 
 shouldReturn​(in: sut.newPasswordTextField)
 
 XCTAssertTrue​(sut.confirmPasswordTextField.isFirstResponder)
 }

Hitting Return from the Confirmation field should fire off the change password request:

Refactoring/RefactoringTests/ChangePasswordViewControllerTests.swift
 func​ ​test_hittingReturnFromConfirmPassword_shouldRequestPasswordChange​() {
  sut.securityToken = ​"TOKEN"
  sut.oldPasswordTextField.text = ​"OLD456"
  sut.newPasswordTextField.text = ​"NEW456"
  sut.confirmPasswordTextField.text = sut.newPasswordTextField.text
 
 shouldReturn​(in: sut.confirmPasswordTextField)
 
  passwordChanger.​verifyChange​(
  securityToken: ​"TOKEN"​,
  oldPassword: ​"OLD456"​,
  newPassword: ​"NEW456"​)
 }

This last test passes because of the call to the changePassword method. But what if that call moves outside the if statement? Imagine what would happen if we put it right before the return statement. Our tests would still pass.

To make sure this doesn’t happen, let’s add two more tests. Hitting the Return key from the first text field shouldn’t start the change password task, so let’s test that:

Refactoring/RefactoringTests/ChangePasswordViewControllerTests.swift
 func​ ​test_hittingReturnFromOldPassword_shouldNotRequestPasswordChange​() {
 setUpValidPasswordEntries​()
 
 shouldReturn​(in: sut.oldPasswordTextField)
 
  passwordChanger.​verifyChangeNeverCalled​()
 }

Then duplicate this test using the new password field.

Congratulations, we’re finally done bringing the code under test! That was a long haul, so put this book down and take a break. When you come back, let’s start refactoring the production code.