Hitesh
Hitesh

Reputation: 896

Could not load Main.Storyboard in XCUITest

I am try to make UITestCase for UIViewcontrollers, But when I load main storyboard in my QuizAppUITests, It could not identify from Bundle and it's gives below error

Could not find a storyboard named 'Main' in bundle NSBundle </Users/mac/Library/Developer/CoreSimulator/Devices/CC199C69-F398-4A7C-882E-BFD3E72B95D3/data/Containers/Bundle/Application/BCC3F88D-E08C-4491-8340-699EAF98AB28/QuizAppUITests-Runner.app> (loaded) (NSInvalidArgumentException)

enter image description here

I have added Main Storyboard in QuizAppUITests Target as well And below is my code for test

import XCTest
@testable import QuizApp

class QuizViewControllerUITests: XCTestCase {
    func makeSUT() -> QuizViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: type(of: self)))
        let sut = storyboard.instantiateViewController(withIdentifier: "QuizViewController") as! QuizViewController
        _ = sut.view
        return sut
    }
    
    func test_loadQuizViewController() {
        let sut = makeSUT()
        sut.headerQuestion = "Q1"
        XCTAssertEqual(sut.headerQuestion, "Q1")
    }

}

Is there any required to change in BuildSetting of QuizAppUITests Target?

Upvotes: 0

Views: 261

Answers (1)

Jon Reid
Jon Reid

Reputation: 20980

None of this makes sense for a UI test. Instead, put your code into your unit test target, which is QuizAppTests.

My book iOS Unit Testing by Example has a chapter called "Load View Controllers". Looking there for what it says about storyboard-based view controllers:

  • Don't include the storyboard in your test target. Put it only in your app target.
  • Then you can load the storyboard with UIStoryboard(name: "Main", bundle: nil)
  • There's a new way to instantiate the view controller that doesn't require force-casting. Instead of instantiateViewController(withIdentifier:), use instantiateViewController(identifier:) and assign it to an explicitly typed variable.

Like this:

let sut: QuizViewController = storyboard.instantiateViewController(
    identifier: "QuizViewController"
)

Since you use the name of the class as the identifier, we can even get rid of the string. This protects us from typos, at compile time:

let sut: QuizViewController = storyboard.instantiateViewController(
    identifier: String(describing: QuizViewController.self)
)

Finally, instead of _ = sut.view, we can be more explicit about loading the view:

sut.loadViewIfNeeded()

This hooks up the outlet connections.

Again, all this belongs in your unit test target QuizAppTests, not your UI test target QuizAppUITests.

For your actual tests, don't just assign a property in your system under test and check that it was assigned. That doesn't prove anything. Instead, I'd focus on testing:

  • That outlets are not nil
  • That interacting with controls does what you want
  • That navigation works
  • That view appearance hasn't changed from an approved snapshot

This can all be done with TDD.

Upvotes: 2

Related Questions