Testing code that uses DispatchQueue.main.async | iOS Lead Essentials Community Q&A
/In this episode, Caio replies to a question we received from Tulio in the private iOS Lead Essentials Slack community:
"How can I test code that dispatches work to the main DispatchQueue asynchronously? If I remove the thread handling code, my test succeeds."
To illustrate, imagine you need to test a View Controller
that loads some text from a Service
and renders it on a UILabel
. The label should display the string "Loading…" until the service completes the request:
public class ViewController: UIViewController {
@IBOutlet public var label: UILabel!
private var service: Service!
public override func viewDidLoad() {
super.viewDidLoad()
label.text = "Loading..."
service.load { [weak self] text in
self?.label.text = text
}
}
}
And here's the test:
func test_viewDidLoad_rendersStringFromService() {
let service = ServiceSpy()
let sut = ViewController.make(service: service)
sut.loadViewIfNeeded()
XCTAssertEqual(sut.label.text, "Loading...")
service.completion?("a string")
XCTAssertEqual(sut.label.text, "a string")
}
The test passes, but the production service completes in a background queue, and the UI needs to be updated in the main queue.
To solve this, you can dispatch the work to the main thread inside the service completion closure:
service.load { [weak self] text in
DispatchQueue.main.async {
self?.label.text = text
}
}
But… Now the test fails because the assertion runs before the main.async
block.
Watch now the full video to find out how to move threading logic away from the services and the UI using a Decorator, and how to deal with this threading challenge in legacy codebases where it's not easy to wrap services.
Subscribe to our YouTube channel and don't miss out on new episodes.
References
- Design Patterns in iOS/Swift iOS Lead Essentials Podcast #014
- iOS Composition Root iOS Lead Essentials Podcast #015
- How to Build iOS Apps with Swift, TDD & Clean Architecture YouTube series
- Join us in the Essential Developer Academy