I am trying to unit test the wiring of button taps in a UIViewController
but I'm finding these tests fail even though the code in the running app works fine.
I've simplified the failing test by removing the view controller and such leaving simply:
import XCTest
class ButtonTest: XCTestCase {
var gotTap: XCTestExpectation!
func test_givenButtonWithTargetForTapAction_whenButtonIsSentTapAction_thenTargetIsCalled() {
gotTap = expectation(description: "Button tap recieved")
let button = UIButton()
button.addTarget(self, action: #selector(tap), for: .touchUpInside)
button.sendActions(for: .touchUpInside)
// Fails.
wait(for: [gotTap], timeout: 0.1)
}
@objc func tap() {
gotTap.fulfill()
}
}
The the test does:
- Wires up an action to a button that fulfils a test expectation
- Taps the button using
button.sendActions(for: .touchUpInside)
- Waits for the expectations.
The failure is:
Asynchronous wait failed: Exceeded timeout of 0.1 seconds, with unfulfilled expectations: "Button tap recieved".
I don't want to use a UI test fo this. They are many orders of magnitude slower to execute, and a unit test should be ideal here.
Questions
- Is this failing because the
UIButton
action sending requires some additional setup of the responder chain? Or a run loop that is not present here? Or something else? How can I set this up minimally in a unit test without instantiating a complete running app? - I initially tried to synchronously test button taps without an expectation (this also didn't work so I thought I'd try asynchronously) – if you can help to get this working, please also indicate if action sending is synchronous or asynchronous.