Could not load Main.Storyboard in XCUITest - swift

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.

Related

instantiateViewController throws SIGABRT on Unit Test

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

Quicklook always displays "no file to preview" error (url is valid)

I'm trying to use QuickLookController subclass as a child controller, setting its view as a subview in the parent. However, it always displays "no file to preview" message in the opening window. URL in the data source is valid, but the controller is never trying to get it! func previewItemAt index is never invoked!
func "numberOfPreviewItems" invokes always.
Please, help!
I get it. driven by example in article https://williamboles.me/hosting-viewcontrollers-in-cells/ I loaded my controller from bundle:
static func createFromStoryBoard() -> PreviewControler {
let storyboard = UIStoryboard(name: "PreviewControler", bundle: Bundle(for: PreviewControler.self))
guard let viewController = storyboard.instantiateViewController(withIdentifier: "PreviewControler") as? PreviewControler else {
fatalError("PreviewControler should be present in storyboard")
}
return viewController
}
But QuickLook controller must be created with it's constructor, so change to
let viewController = PreviewController()
solved the problem. Now all is fine.

All ViewController Views are Nil in Unit Testing?

Sorry if this is a beginner question but I'm relatively new to Unit testing and didn't see this asked anywhere.
When I start my unit tests in Swift, I setUp my tests by instantiating my viewController.
My code is set up using MVVM (Model - View - ViewModel). So when I test some of my viewModel methods, they will update the Views (in the ViewController) in the UI. The problem is, Xcode keeps crashing and says that the views in the ViewController are nil? How do I prevent these views from being nil? Am I doing something wrong? How do I instantiate the views within the viewController? I thought this would be automatic.
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.tattooModel = ARModel(imageName: "blank", tattooType: .new)
self.tattooViewModel = ARViewModel(tattooModel: tattooModel!, delegate: viewController!)
self.mainUIModel = MainUIModel()
self.mainUIViewModel = MainUIViewModel(model: mainUIModel!, delegate: viewController!)
}
Turns out I was missing the self.viewController?.loadView() call in the setUp() method.
The correct code that works without nil views is as follows:
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.viewController?.loadView()

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.

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.