Can I check IBAction?

This is easy for unit test IBOutlets, but what about IBActions? I tried to find a way to do this, but with no luck. Is there a way to connect unit test between IBAction in the view controller and the button in the nib file?

+4
source share
7 answers

I did this using OCMock, for example:

MyViewController *mainView = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; [mainView view]; id mock = [OCMockObject partialMockForObject:mainView]; //testButtonPressed IBAction should be triggered [[mock expect] testButtonPressed:[OCMArg any]]; //simulate button press [mainView.testButton sendActionsForControlEvents: UIControlEventTouchUpInside]; [mock verify]; 

If IBAction is not connected, the test will fail with the error "the expected method was not called."

+5
source

To fully test the modules, each release / action requires three tests:

  • Is the output connected to viewing?
  • Is the output connected to the desired action?
  • Invoke the action directly, as if it were triggered by an exit.

I do this all the time for TDD view controllers. You can see an example in this screencast .

It sounds like you're asking specifically for the second step. Here is a unit test example confirming that touching inside myButton triggers the doSomething: action doSomething: Here, as I express it, using OCHamcrest . ( sut is the test sut for the system under test.)

 - (void)testMyButtonAction { assertThat([sut.myButton actionsForTarget:sut forControlEvent:UIControlEventTouchUpInside], contains(@"doSomething:", nil)); } 

Alternatively, here is the version without Hamcrest:

 - (void)testMyButtonAction { NSArray *actions = [sut.myButton actionsForTarget:sut forControlEvent:UIControlEventTouchUpInside]; XCTAssertTrue([actions containsObject:@"doSomething:"]); } 
+9
source

Here is what I use in Swift. I created a helper function that I can use in all my unit tests of the UIViewController:

 func checkActionForOutlet(outlet: UIButton?, actionName: String, event: UIControlEvents, controller: UIViewController)->Bool{ if let unwrappedButton = outlet { if let actions: [String] = unwrappedButton.actionsForTarget(controller, forControlEvent: event)! as [String] { return(actions.contains(actionName)) } } return false } 

And then I just call it from the test as follows:

 func testScheduleActionIsConnected() { XCTAssertTrue(checkActionForOutlet(controller.btnScheduleOrder, actionName: "scheduleOrder", event: UIControlEvents.TouchUpInside, controller: controller )) } 

Basically, I'm sure the btnScheduleOrder button has an IBAction associated with the scheduleOrder name for the TouchUpInside event. I need to pass the controller where the button is contained as a way to check the target for the action.

You can also make it a bit more complicated by adding another else clause in case the Button doesn't expand, which means the outlet is not there. Since I like to separate output tests and actions that I haven't included here,

+3
source

Thus, it is possible to create an instance of the view controller from the storyboard or tip, and then touch UIButton. However, I would not do this because then you are testing the Apple API. Rather, I would check by calling the method directly. For example, if you have a method - (IBAction)foo:(id)sender in your view controller, and you need to check the logic in this method, I would do something like this:

 MyViewController *viewController = [[MyViewController alloc] initWithNibName:@"NibName" bundle:[NSBundle mainBundle]]; UIButton *sampleButton = [[UIButton alloc] init]; [sampleButton setTitle:@"Default Storyboard Title" forState:UIControlStateNormal]; [viewController foo:sampleButton]; // Run asserts now on the logic in foo: 
+1
source

Julio Bayon answered above, translated for Swift 3:

  func checkActionForButton(_ button: UIButton?, actionName: String, event: UIControlEvents = UIControlEvents.touchUpInside, target: UIViewController) -> Bool { if let unwrappedButton = button, let actions = unwrappedButton.actions(forTarget: target, forControlEvent: event) { var testAction = actionName if let trimmedActionName = actionName.components(separatedBy: ":").first { testAction = trimmedActionName } return (!actions.filter { $0.contains(testAction) }.isEmpty) } return false } 
+1
source

There are many good answers here. What works best for you depends on your test plan.

Since this question turned into a survey of testing methods, here's another one: if you want to check the results of manipulating your application UI, check out the UI Automation Tool in Tools .

-one
source

Of course, just put the NSLog statement somewhere in the action enclosure. If the action works, you will see the output in the console.

-3
source

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


All Articles