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.
| 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:
| 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:
| @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:
| 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:
| 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:
| 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.