I can’t test that! (Part II)

Daniel Sedam
2 min readNov 23, 2020

Tips and Tricks for writing better, more testable swift code.

Keeping your tests in a passing state can sometimes be a challenge. Just like your code needs maintenance, so do your tests!

You might introduce new code that changes the functionality of another piece of code. When this happens tests may start failing. Great! The tests did their job! They pointed out a regression on how something use to work.

Tests can also fail because they were written poorly or incorrectly. I see this a lot when writing tests for asynchronous code.

“Its not easy being green” ~ Kermit the Frog

In this article I’m going to show you how to test that an UIAlertView was presented and has the correct title and description.

class TicketViewController: UIViewController {    func submitTicket(validator: Validator) {
guard validator.isTicketValid(ticket) else {
let alert = UIAlertController(title: “Invalid Ticket”,
message: “Missing inputs.”,
preferredStyle: .alert)
let action = UIAlertAction(title: “OK”,
style: .default,
handler: nil)
alert.addAction(action) self.present(alert, animated: true)
return
}

...
}
}

Above we have a function that takes in a Validator which makes sure the ticket we want to submit has everything that is required. If it fails we show an Alert.

When writing tests for asynchronous code, Apple gives us Expectations. The following example uses Expectations with NSPredicates.

class TicketViewControllerTests: XCTestCase {    func testSubmitTicket() {        // Given
let ticketVC = TicketViewController()
let mockValidator = MockValidator()
let predicate = NSPredicate(format: “presentedViewController != nil”)
_ = expectation(for: predicate,
evaluatedWith: ticketVC,
handler: nil)
// When
ticketVC.submitTicket(validator: mockValidator)
// Then
waitForExpectations(timeout: 5, handler: nil)
if let alertVC = ticketVC.presentedViewController as? UIAlertController { XCTAssertEqual(alertVC.title, “Invalid Ticket”)
} else {
XCTFail()
}
}
}

So we initialized our Expectation with a Predicate that continually checks for our ViewController to have a presentedViewController. Once this condition is met our Expectation is fulfilled and we can assert a few things to complete our test!

*Note: please take note on the order of things. It is important to create your expectation before triggering the function that you are testing. If you call submitTicket() and then set up the expectation you open yourself to a timing window and your test might fail sometimes.

Thank you for reading! If you enjoyed this article or learned something new please give it a clap. Until next time, keep writing tests and keep those pipelines green!

If you missed the first article in this series check it out!

--

--