6

Here I'm trying to check unit test cases for view controller. - I have a view controller with button and label. - When you click on the button, it will call another method. which feeds the data to the button action label text change.
- I want to check that button triggered that method or not? without adding any boolean or return type of the function.

Here is my code.

class ViewController: UIViewController {

    @IBOutlet weak var buttonFetch: UIButton?

    @IBOutlet weak var nameLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    @IBAction func fetchUser() {
        self.nameLabel.text = self.getUser()
    }

    func getUser() ->String {
        return User.data()
    }
}

struct User  {
    static func data()->String{
        return "Apple"
    }
}

Here is my test case

func testFetchUserAction() {
        controller.buttonFetch?.sendActions(for: .touchDown)
        // I want to test the method getUser from viewcontroller gets called or not
        // some thing like this XCTAssert(self.controller.getUser(),"not called")
        XCTAssertEqual(self.controller.nameLabel.text!, "Apple")

    }
Jon Reid
  • 20,545
  • 2
  • 64
  • 95
Damodar
  • 707
  • 2
  • 10
  • 23

5 Answers5

4

Have you tried as like below..

func testFetchUserAction() {

    self.controller.nameLabel.text = nil

    //Use this line, If you assigned touchUpInside to your button action
    controller.buttonFetch?.sendActions(for: .touchUpInside)

    //Use this line, If you assigned touchDown to your button action
    controller.buttonFetch?.sendActions(for: .touchDown)

    XCTAssert(self.controller.nameLabel.text != nil, "not called")

}

Note: To make test case failed purposely, you can change UIControl.Event in sendActions

Natarajan
  • 3,241
  • 3
  • 17
  • 34
4

What you're missing:

Make sure to call loadViewIfNeeded() somewhere.

This can be in the test, or in setUp(). This will load your outlets, and call viewDidLoad().

Then, as much as possible, test the result of invoking an action, instead of whether or not a method was called. You've done this in your example assertion.

I'd also add that the correct action for buttons is almost always .touchUpInside, not .touchDown. This allows the user to press a button, then drag away to change their mind. "I don't want to push this after all."

Jon Reid
  • 20,545
  • 2
  • 64
  • 95
1

Found something similar on another response, attempted something like this? Source: How to add tap action for button in "Unit Testing" and show Alert

func testTappingLoginButton_ShouldPresentAlert() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sut = storyboard.instantiateInitialViewController() as! ViewController
    sut.loadViewIfNeeded()
    let alertVerifier = QCOMockAlertVerifier()

    sut.loginButton.sendActions(for: .touchUpInside)

    XCTAssertEqual(alertVerifier.presentedCount, 1)
}

Let me know if it works or not!

0

I have tried like below, its passed the test. But I'm not sure it is correct or not?

XCTAssertNotNil(self.controller!.getUser, "get user method not called")
Damodar
  • 707
  • 2
  • 10
  • 23
  • Don't post updates to your question as an answer to you question. And please read [how to ask good questions](https://stackoverflow.com/help/how-to-ask). – dasdom Dec 13 '18 at 19:48
0

Fake tap action on a button for UIViewController testing.

By using this helper, tap(_:), you can verify if your button perform the @IBAction you were expecting when testing your ViewController.

public func tap(_ button: UIButton) {
  button.sendActions(for: .touchUpInside)
}
// Button in your UIViewController
@IBOutlet weak var primaryButton: UIButton!

// In your XCTest subclass
var sut: UIViewController!

tap(sut.primaryButton) // example of use in your test() function
Roland Lariotte
  • 2,606
  • 1
  • 16
  • 40