This is my simple test case:
import XCTest
@testable import MyApp //it doesn't work
because of this:
class TabBarControllerTests: XCTestCase {
override func setUp() {
super.setUp()
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject([], forKey: DBTabBarOrderedIndexesKey) //key is undefined, because of lack of my app module
defaults.synchronize()
continueAfterFailure = false
XCUIApplication().launch()
}
func testIsOrderOfTabsSaved() {
XCUIApplication().tabBars.buttons["Catering"].tap()
//what next?
}
}
Once I tap UITabBarItem
I change the value of DBAppSettings.mode
, so here I would like to have an access to my DBAppSettings.mode
property to check if it is really changed.
I noticed that there is one weird thing, when I build my app, and check what was built, there is no build for my UITest target. Is it important?
This is a response from Apple:
UI tests execute differently from Unit tests - Unit tests run inside your application process so they can access your application code. UI tests execute in a separate process, outside your application, so they can simulate how the user interacts with the application. It’s not expected that you will be able to access your app class from a UI test.
Every object you need access to in UI Tests must be part of the UI Test target. This includes object dependencies. It's a slippery slope, and rather a mess.
Rather than having your tests know about your app, try turning it around and have your app know that it's being tested. One way is to use the launchArguments
property:
app = XCUIApplication()
app.launchArguments.append("TestMode")
app.launch()
Then in your app:
if NSProcessInfo.processInfo().arguments.contains("TestMode") {
// I am running in test mode
}
In your case the app could then set the NSUserDefaults accordingly.
Since Apple prevents you from accessing your main application from the UI tests, you might think about reorganizing your application structure to store the relevant data which needs checking into a spot accessible to the UI tests.
You might consider moving definitions and data from your main application class to a separate class which can be loaded by the test framework.
I solved the inaccessible symbol problem by pulling some stuff from my app target into two frameworks (model and view-model) that I can then import into my UI tests.
As for accessing the actual memory of the executable, you can't, but you can test for things that should appear on screen somewhere in that mode. I use this assertion to e.g. check for the existence of a table view cell:
XCTAssertTrue(tables.cells.staticTexts["Cell title I expect to exist"].waitForExistence(timeout: 1))
Since you can use accessibility you can access a pretty decent amount of things this way. I suppose you could add an invisible label with a dump of your app's memory-in test mode only!
I too make use of the launch arguments to configure my app for UI tests, as Michael suggested: https://stackoverflow.com/a/34469136/4789448.