How are TDD UIGestureRecognizers?

When I tried a TDD code that would otherwise use UIGestureRecognizer, I did not find the ability to programmatically verify the target action. Without this, I’m not sure that I can test it correctly.

If the gesture recognizer is installed in IB (with support for iOS 5+), the target action is configured when loading NIB / Storyboard; if this is done in code, it uses initWithTarget: action :, both of which mean that no amount of ridicule can detect the target action.

I have no ideas. If someone successfully tested the UIGestureRecognizer, I could take a tip.

+6
source share
4 answers

Unfortunately, you are trying to test a framework class that was not written for testing, and therefore does not reveal the state you want to check. This makes it difficult to claim that there are pairs of target actions that you want to test. In this case, I have three options that you can use, none of which are a great solution:

Perhaps you can subclass UIGestureRecognizer, override target action methods to save registered pairs in a collection, which you can then provide to users of this class, and then call the superclasses for implementing these methods. Unfortunately, you are introducing new classes just to make testing easier, keep in mind to use them and you may have to send them from UIGestureRecognizer to your own subclass depending on where you get the gesture recognizer link from.

Alternatively, your test can run new versions of target action methods in UIGestureRecognizer, which gives you the ability to track added targets. Just make sure you replace the original method implementations with your place when you are done, or future tests will have unexpected behavior.

Finally, you can find a private API call that gives you the ability to check registered target actions on a gesture recognizer. Just make sure that the private API call remains only in your test code.

+4
source

Here's how I unit test tap gesture recognizer in Swift. The test ensures that code that responds to a click 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 the click gesture using the view and close the callback

 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 mimic the tap in my unit test and verify that the closure was caused

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

Please note that this is a unit testing method. In addition, I use UI tests to make sure that all parts work together in a sweet harmony.

+2
source

I suggest subclassing 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) } } 
+1
source

If someone is interested in a solution that uses a private API, the following category in UIGestureRecognizer can be used to send gesture recognition actions. As Jonah points out, do not use it in production code.

 @interface GGGestureRecognizerTarget : NSObject{ @public id _target; SEL _action; } @end @implementation GGGestureRecognizerTarget @end @interface UIGestureRecognizer (GGTest) - (void)sendActions; @end @implementation UIGestureRecognizer (GGTest) - (void)sendActions{ id targets = [self valueForKey:@"_targets"]; for (GGGestureRecognizerTarget* target in targets) { [self sendActionToTarget:target]; } } - (void)sendActionToTarget:(GGGestureRecognizerTarget*)recognizerTarget{ id target = recognizerTarget->_target; SEL action = recognizerTarget->_action; [target performSelector:action withObject:self]; } @end 
0
source

Source: https://habr.com/ru/post/905050/


All Articles