I'm writing unit tests for various components of a UI. However, I'm running into trouble when writing tests for buttons that trigger asynchronous functions. My problem is that I'm using the UIButton.sendActions(for controlEvents: UIControlEvents)
to trigger the pressing of the button, which then calls an asynchronous function.
Say I have a test:
func testLoginToMainScene() {
loadView()
let queue = DispatchQueue(label: "LoginButtonPressed")
queue.sync {
view.loginButton.sendActions(for: .touchUpInside)
}
XCTAssertTrue(router.navigateToMainSceneCalled)
}
That tests the following bit of code in a LoginViewController
class:
@IBAction func loginButtonPressed(_ sender: AnyObject) {
hideKeyboard()
performLogin(email: emailTextField.text, password: passwordTextField.text)
}
And a function that handles the login by calling a method of a redux worker:
private func performLogin(email: String, password: String) {
let result = myReduxWorker.getStore().dispatch(newLoginAction(email: email, password: password)
if let promise = result as? Promise<[String: Any]> {
promise.done { json -> Void in
//Login was successful!
router.navigateToMainScene()
}
}
Currently, the test fails because the XCTAssertTrue
test runs before the performLogin
function completes, and thus before navigateToMainScene
is called. I tried using a DispatchQueue
, but as soon as the .touchUpInside
action is sent to the button, the block of code inside the .sync
finishes, and the test function continues and runs the XCTAssertTrue
test.
What is the best way to ensure that the performLogin
function has finished running before executing the test case?