How to TDD UIGestureRecognizers? How to TDD UIGestureRecognizers? xcode xcode

How to TDD UIGestureRecognizers?


Unfortunately you're trying to inspect a framework class which wasn't written with testing in mind and so doesn't expose the state you want to verify. That is going to make it difficult to assert on the existence of the target-action pairs you want to check for. In such a case I have three options you might use, none of which are great solutions:

You might be able to subclass UIGestureRecognizer, override the target-action methods to save the registered pairs in a collection you can then expose to users of the class, and then call the superclass implementations of those methods. Unfortunately then you're introducing new classes just to make testing easier, have to remember to use them, and may have to cast from UIGestureRecognizer to your custom subclass depending on where you get a gesture recognizer reference from.

Alternately your test could swizzle new versions of the target-action methods into UIGestureRecognizer giving you a hook to track added targets. Just make sure to swap the original method implementations back into place when you're done or future tests will have unexpected behavior.

Finally you might be able to find a private API call which gives you a way to check the registered target-actions on the gesture recognizer. Just make sure that private API call remains only in your test code.


This is how I unit test tap gesture recognizer in Swift. The test makes sure the code that responds to the tap gesture is executed.

First, I create an OnTap helper class

class OnTap: NSObject {  var closure: ()->()  init(view: UIView, gesture: UIGestureRecognizer, closure:() -> ()) {    self.closure = closure    super.init()    view.userInteractionEnabled = true    view.addGestureRecognizer(gesture)    gesture.addTarget(self, action: "didTap:")  }  func didTap(gesture: UIGestureRecognizer) {    closure()  }}

Next, I register a tap gesture wit a view and a callback closure

class MyClass {  var onTap: OnTap?  var didTap = false  func setupTap() {    let myView = UIView()    onTap = OnTap(view: myView, gesture: UITapGestureRecognizer()) { [weak self] in      self?.didTap = true    }  }}

Finally, I simulate the tap in my unit test and check that the closure has been called

class MyGestureTests: XCTestCase {  func testRepondToGesture() {    let obj = MyClass()    obj.setupTap()    obj.onTap?.didTap(UITapGestureRecognizer())    XCTAssert(obj.didTap)  }}

Note, this is a unit testing technique. In addition, I use UI tests to make sure all pieces work together in sweet harmony.


I'd suggest to subclass the gesture recognizer as follows:

class PanGestureRecognizer: UIPanGestureRecognizer {    let initialTarget: Any?    let initialAction: Selector?    public override init(target: Any?, action: Selector?) {        initialTarget = target        initialAction = action        super.init(target: target, action: action)    }}