instantiateViewController throws SIGABRT on Unit Test - swift

I'm writing unit tests for a controller that has elements (like buttons) created in storyboard. I'm trying to instantiate the view controller in my Unit Test so I can access those elements and not have the app crash. However, the way that I am trying to instantiate my storyboard is causing Xcode to throw a SIGABRT error on the line that I am calling the instantiateViewController command.
This is the code that I am using to try and instantiate my storyboard in my unit test file:
func testAreaActionsViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: self.classForCoder))
let viewController = storyboard.instantiateViewController(withIdentifier: "AreaActionsViewController") as! AreaActionsViewController
// view.loadView()
//
// view.viewDidLoad()
}
Does anyone have an idea as to why it is throwing SIGABRT? I verified in my storyboard that the Identifier is AreaActionsViewController, and also set the target in my Main.storyboard to include my testing target.

Sorry for late but I had same issue and I solved removing this:
#testable import ProjectName

Related

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)
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?
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.

What is the use of beforeEach method in Kiwi/Quick test frameworks?

Why is beforeEach used in test frameworks like Kiwi and Quick?
beforeEach {
let storyboard = UIStoryboard(name: "Main",
bundle: NSBundle(forClass: self.dynamicType))
let navigationController = storyboard.instantiateInitialViewController() as UINavigationController
viewController = navigationController.topViewController as ViewController
UIApplication.sharedApplication().keyWindow!.rootViewController = navigationController
let _ = navigationController.view
let _ = viewController.view
}
Example code taken from: https://www.natashatherobot.com/unit-testing-in-swift-a-quick-look-at-quick/
Is the equivalent of Apple's Test Framework setUp method. It is executed before every test, to set up components and mocks that will be used in every test.
According to Kiwi's wiki:
beforeEach(aBlock) is run before every it block in all enclosed contexts. Code that actually sets up the particular context should go here.
It's helpful to avoid code repetition and that your tests have a standard setup among them.
In the piece of code that you shared, the beforeEach method is setting up a UINavigationController with a rootViewController extracted from the storyboard. It is also loading the view so the tests can access components such as outlets.

Cannot Programmatically Switch Storyboards

I'm attempting to switch storyboards when a button only if a condition (The textfield not being empty) is met. I found this piece of code as an answer to a similar question, and it doesn't seem to be working. My program crashes with "'Storyboard () doesn't contain a view controller with identifier 'Avatar Controller''"
I'm not completely sure as to where to find the view controller identifies, or whether or not I have to add '.storyboard' at the end of the storyboard name.
let storyboard = UIStoryboard(name: "Avatar", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "Avatar Controller")
self.navigationController!.pushViewController(vc, animated: true)
To make it works, Use Storyboard ID has to be checked
This could be happening for 2 reasons.
1) Because in your Avatar.storyboard you are referencing a view controller with the id Avatar Controller but you have not set that value on any of the view controllers in the storyboard file. Go into your Avatar.storyboard, click on the view controller you want to show and go to this panel:
Make sure that the Storyboard ID for your viewController matches what value you use in storyboard.instantiateViewController(withIdentifier: "Avatar Controller")
2) Because you are not providing a bundle when you are loading your storyboard. You are calling this
let storyboard = UIStoryboard(name: "Avatar", bundle: nil);
but because you specify bundle as nil it doesn't know where to look for the Avatar.storyboard file. To fix it, specify bundle as Bundle.main:
let storyboard = UIStoryboard(name: "Avatar", bundle: Bundle.main);

Swift Unit Test- Wait for viewDidLoad

I am new to Unit test in Swift. Now I wanted to test my viewController. In viewDidLoad I have an asynchronous call. So that if I want to test my Controller if the data got loaded correctly, the data didn't got loaded. I already read that I have to build in an XCTestExpectation.
So the information I have, I got from this question: XCTest Unit Test data response not set in test after viewDidLoad
The answer there is an example from which I don't know how to implement. My test class looks like this:
import XCTest
#testable import apirequest
class SearchedForViewControllerTests: XCTestCase {
var vc: SearchedForViewController!
override func setUp() {
super.setUp()
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
vc = storyboard.instantiateViewController(withIdentifier: "SearchedForViewController") as! SearchedForViewController
vc.passedString = "garten"
let _ = vc.view
}
func testArticlesShwon() {
print(vc.tableView.numberOfRows(inSection: 0))
}
}
So if I look on my own code the part of viewDidLoad happens in
let _ = vc.view
If I want to build in a Expectation, I have to wait for this part. But the part is not a function. So I don't know how I could tell my Expectation to fulfill after loading.
This may be more of an opinion/design answer, but I would highly recommend testing your ViewController and your Model completely separately.
E.g. when you're testing your VC, manually set the data in your tests, then separately have tests to ensure your model and any networking/async calls are functioning properly.

Add storyboard in Swift

How do you create multiple storyboards in the same project using Swift? I know I have to go to File -> New -> File and then choose Storyboard, but how do I integrate that storyboard into my project and navigate to the new storyboard's views in Swift code?
You need to get a reference to the desired storyboard
let aStoryboard = UIStoryboard(name: "storyboardName", bundle: nil)
Then instantiate desired view controller
let vc = aStoryboard.instantiateViewControllerWithIdentifier("viewControllerIdentifier") as! UIViewController