How do I keep the app open between UITests in Xcode - xcode-ui-testing

I have a series of UITests I want to run as individual tests but I don't want to relaunch the app between each test. How can I launch the app and keep it open so it doesn't shutdown and restart between tests.
I tried putting XCUIApplication().launch() in init() but got an error.

To improve on #James Goe's answer -
To avoid all those relaunches, but keep the ability to run individual tests - and not care about the order in which they're run - you can:
class MyTests: XCTestCase {
static var launched = false
override func setUp() {
if (!MyTests.launched) {
app.launch()
MyTests.launched = true
}
(Of course, now that you're not relaunching your app you have to care much more about state, but that's another story. It's certainly faster.)

In your setUp() method, remove [[[XCUIApplication alloc] init] launch]; and put it into the first test you will be performing.
For eg.,
If you have tests: testUI(), testUIPart2(), testUIPart3(), etc., and it runs in this order, put [[[XCUIApplication alloc] init] launch]; into the first line of testUI() and no where else.

This question is a bit old, but XCTestCase has a class func for setup() and tearDown() that executes before the first test, and after the last test completes. If you create your XCUIApplication() as a singleton (say XCUIAppManager), you could put your XCUIAppManager.shared.launchApp() call within the override class func setup() method... this will only launch the app once per XCTestCase (contains multiple tests)
override class func setUp() { // 1.
super.setUp()
// This is the setUp() class method.
// It is called before the first test method begins.
// Set up any overall initial state here.
}
https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods
Example app manager singleton implementation:
import XCTest
class XCUIAppManager {
static let shared = XCUIAppManager()
let app = XCUIApplication(bundleIdentifier: "com.something.yourAppBundleIdentifierHere")
private(set) var isLaunched = false
private init() {}
func launchApp() {
app.launch()
isLaunched = true
}
}

Here's another way to allow both common or non-common app launches on each test.
you can do it by creating your custom XCUIApplication and include state in your app launches as shown below.
import XCTest
class MyUIApplication: XCUIApplication {
private(set) var isLaunched = false
static let shared = MyUIApplication()
private override init() {
super.init()
// add your launch arguments and environment keys
launchArguments += ["isRunningTests"]
launchEnvironment["state"] = "InitState"
}
func launchIfNeeded(at state: AppState) {
if !isLaunched {
launch(at: state)
}
}
func launch(at state: AppState) {
switch state {
case .home:
launchEnvironment["state"] = "InitState"
...
}
launch()
isLaunched = true
}
}
An example of test case with the above solution:
class MyViewControllerTests: XCTestCase {
let app = VFGUIApplication.shared
override func setUp() {
super.setUp()
app.launchIfNeeded(at: .home)
}
}
This is preferable specially if you are working with a team and you want to make sure that launches are executed properly based on pre-defined launchArguments and launchEnvironment, etc.

Related

Testing UserDefaults values in UiTests or UnitTests

I am saving an Int in UserDefaults and this is reduced by one by clicking a button. I don't know if that is important but I have added an extension to UserDefaults to load an initial value if the app starts for the first time:
extension UserDefaults {
public func optionalInt(forKey defaultName: String) -> Int? {
let defaults = self
if let value = defaults.value(forKey: defaultName) {
return value as? Int
}
return nil
}
}
The UserDefaults are used as ObservableObject and accessed as EnvironmentObject within the app like this:
class Preferences: ObservableObject {
#Published var counter: Int = UserDefaults.standard.optionalInt(forKey: COUNTER_KEY) ?? COUNTER_DEFAULT_VALUE {
didSet {
UserDefaults.standard.set(counter, forKey: COUNTER_KEY)
}
}
}
I am now trying to test that the value in the UserDefaults decreases when the button is clicked.
I am trying to read the UserDefaults in the test with:
XCTAssertEqual(UserDefaults.standard.integer(forKey: "COUNTER_KEY"), 9)// default is 10
I have tried it with normal UnitTests where the methods behind the Button are called and with UITests but both do not work. In the UnitTests I get back the COUNTER_DEFAULT_VALUE and in the UiTests I get back 0.
I am trying to access the UserDefaults directly in the test, instead of using the Preferences object, because I have not found a way to access that as it is an ObservableObject.
I have checked in the Emulator that the UserDefaults are saved/loaded correctly when using the app. Is it not possible to access the UserDefaults in the tests or am I doing it wrong?
The key to success is Dependency injection. Instead of directly accessing the shared user defaults object (UserDefaults.standard), declare an object of type UserDefaults within the class:
let userDefaults: UserDefaults
In the view where you declare the model, you are free to use the shared object:
#StateObject var model = Preferences(userDefaults: UserDefaults.standard)
But inside your test, create an dedicated UserDefaults object and pass it to the initializer like so:
let userDefaults = UserDefaults(suiteName: #file)!
userDefaults.removePersistentDomain(forName: #file)
let model = Preferences(userDefaults: userDefaults)
The benefit is clear: You control the state of UserDefaults. And that means the code works in every environment. To keep it simple, I haven't incorporated your extension, yet. But I'm sure you will manage to get it working.
TL;DR
Please see my working example below:
ContentView.swift
import SwiftUI
import Foundation
class Preferences: ObservableObject {
let userDefaults: UserDefaults
#Published var counter: Int {
didSet {
self.userDefaults.set(counter, forKey: "myKey")
}
}
init(userDefaults: UserDefaults) {
self.userDefaults = userDefaults
self.counter = userDefaults.integer(forKey: "myKey")
}
func decreaseCounter() {
self.counter -= 1
}
}
struct ContentView: View {
#StateObject var model = Preferences(userDefaults: UserDefaults.standard)
var body: some View {
HStack {
Text("Value: \(self.model.counter)")
Button("Decrease") {
self.model.decreaseCounter()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
InjectionTests.swift
import XCTest
#testable import Injection
class InjectionTests: XCTestCase {
func testPreferences() throws {
// arrange
let userDefaults = UserDefaults(suiteName: #file)!
userDefaults.removePersistentDomain(forName: #file)
let model = Preferences(userDefaults: userDefaults)
// act
let valueBefore = userDefaults.integer(forKey: "myKey")
model.decreaseCounter()
let valueAfter = userDefaults.integer(forKey: "myKey")
// assert
XCTAssertEqual(valueBefore - 1, valueAfter)
}
}

Run code after loop in background ends

I am running background thread in a while loop where I am doing some file handling task. I have some code after the loop. But the codes after the loop being executed before the loop ends (cause I am using background thread). Is there any way I can execute some code exactly after the loop ends?
Here is my code:
while i < testCount {
let task = AsyncTask(
backgroundTask: {
() -> Double in
// some file handling
return 234.09
},
afterTask: {
(val: Double) in
self.showVal(val)
}
);
task.execute();
i += 1
}
// I want to run this code after the loop ends
print("average: \(avg)")
showVal(avg)
My showVal(Double) function
func showVal(val: Double) {
print("val found: \(val)")
display.text = "\(val) found"
}
And here is my AsyncTask class
public class AsyncTask <BGParam,BGResult>{
private var pre:(()->())?;//Optional closure -> called before the backgroundTask
private var bgTask:(param:BGParam)->BGResult;//background task
private var post:((param:BGResult)->())?;//Optional closure -> called after the backgroundTask
public init(beforeTask: (()->())?=nil, backgroundTask: (param:BGParam)->BGResult, afterTask:((param:BGResult)->())?=nil){
self.pre=beforeTask;
self.bgTask=backgroundTask;
self.post=afterTask;
}
public func execute(param:BGParam){
pre?()//if beforeTask exists - invoke it before bgTask
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
let bgResult=self.bgTask(param: param);//execute backgroundTask in background thread
if(self.post != nil){//if afterTask exists - invoke it in UI thread after bgTask
dispatch_async(dispatch_get_main_queue(),{self.post!(param: bgResult)});
}
});
}
}
Note: I am a beginner in Swift.
Edit:
I actually want to do:
A background file handling task
After the task ends, I want to show a text in a UILabel
I need to do this two tasks several times (say 100 times). If swift has easier methods for my purpose, please advise.
I'll try with some pseudo-code based our discussion to see if that might help you on the way. The manager-object would probably look a little something like this:
class RequestManager {
private let requiredNumberOfRequests: Int
private let completionHandler: ()->()
private var counter: Int {
didSet {
if counter == requiredNumberOfRequests {
completionHandler()
}
}
}
init(numberOfRequests: Int, completionHandler: ()->()) {
self.requiredNumberOfRequests = numberOfRequests
self.completionHandler = completionHandler
self.counter = 0
}
// Don't know exactly what you want to do here, but something like...
func performRequest(success: (value: Double)->()) {
// Network stuff, on success it looks like you'll have a Double(?)
let value: Double = 234.09
success(value: value)
counter += 1
}
}
Then, before your loop you'll create an instance of the requestManager
let manager = RequestManager(numberOfRequests: 100) { average in
print("average: \(average)")
}
...and call the performRequest as part of your loop.

Subclassing Swift Generic Class with NSObject inheritance

I'm running into an error where I'm not certain if it's a limitation of the Swift language, or a bug. Here's the most basic premise:
class GenericClass<T> : NSObject {
var inputValue: T
init(value: T) {
self.inputValue = value
super.init()
}
}
class SubClass : GenericClass<String> {
override init(value: String) {
super.init(value: value)
}
}
var test = GenericClass(value: "test") //Succeeds
var test2 = SubClass(value: "test2") //Fails with EXC_BAD_ACCESS
I'm getting no compiler warnings here, but the Subclass refuses to instantiate. I have a more complicated goal within subclassing specific contexts of a generic, but this basic issue above is what I boiled it down to.
Interestingly, if I remove the NSObject inheritance on the GenericClass (and the super.init() from the generic's init method), this setup works without issue, so I'm thinking it must have something to do with the fact that I'm inheriting from NSObject. My full implementation MUST inherit from an NSOperation (I'm basically making custom NSOperation classes with a generic superclass), so inheriting from NSObject(i.e. NSOperation) is not optional for me.
It's bothersome that there are no compiler errors and I'm getting something as nasty as a EXC_BAD_ACCESS. It makes me think that maybe this is supposed to work, but isn't currently. I know they only recently began supporting the subclassing of generic classes in Swift. I'm running the latest xCode beta 6.
Any insight appreciated!
I'm pretty sure it's a bug. Interestingly, everything works fine if you delete the initializers, even when you inherit from NSObject:
class GenericClass<T> : NSObject {
}
class SubClass : GenericClass<String> {
}
var test : GenericClass<Int> = GenericClass() // Succeeds
var test2 = SubClass() // Succeeds
var test3 : GenericClass<String> = SubClass() // Succeeds
var test4 : GenericClass<Int> = SubClass() // Fails ("cannot convert SubClass to GenericClass<Int>")
A messy workaround might be to use default protocol implementations, and then extend NSOperation to initialize a new operation from your protocol, but it's probably better to just file a bug report and wait for them to fix this :)
import Foundation
class GenericClass<T> : NSObject {
var inputValue: T
init(value: T) {
print(value)
inputValue = value
super.init()
}
}
class SubClass<T> : GenericClass<T> {
override init(value: T) {
super.init(value: value)
}
}
let test = GenericClass(value: "test") //Succeeds
test.inputValue
let test2 = SubClass(value: "test2") //Succeeds
test2.inputValue
let test3 = GenericClass(value: 3.14) //Succeeds
test3.inputValue
let test4 = SubClass(value: 3.14) //Succeeds

How can I get XCTest to wait for async calls in setUp before tests are run?

I'm writing integration tests in Xcode 6 to go alongside my unit and functional tests. XCTest has a setUp() method that gets called before every test. Great!
It also has XCTestException's which let me write async tests. Also great!
However, I would like to populate my test database with test data before every test and setUp just starts executing tests before the async database call is done.
Is there a way to have setUp wait until my database is ready before it runs tests?
Here's an example of what I have do now. Since setUp returns before the database is done populating I have to duplicate a lot of test code every test:
func test_checkSomethingExists() {
let expectation = expectationWithDescription("")
var expected:DatabaseItem
// Fill out a database with data.
var data = getData()
overwriteDatabase(data, {
// Database populated.
// Do test... in this pseudocode I just check something...
db.retrieveDatabaseItem({ expected in
XCTAssertNotNil(expected)
expectation.fulfill()
})
})
waitForExpectationsWithTimeout(5.0) { (error) in
if error != nil {
XCTFail(error.localizedDescription)
}
}
}
Here's what I would like:
class MyTestCase: XCTestCase {
override func setUp() {
super.setUp()
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
var data = getData()
db.overwriteDatabase(data, onDone: () -> () {
// When database done, do something that causes setUp to end
// and start running tests
})
}
func test_checkSomethingExists() {
let expectation = expectationWithDescription("")
var expected:DatabaseItem
// Do test... in this pseudocode I just check something...
db.retrieveDatabaseItem({ expected in
XCTAssertNotNil(expected)
expectation.fulfill()
})
waitForExpectationsWithTimeout(5.0) { (error) in
if error != nil {
XCTFail(error.localizedDescription)
}
}
}
}
Rather than using semaphores or blocking loops, you can use the same waitForExpectationsWithTimeout:handler: function you use in your async test cases.
// Swift
override func setUp() {
super.setUp()
let exp = expectation(description: "\(#function)\(#line)")
// Issue an async request
let data = getData()
db.overwriteDatabase(data) {
// do some stuff
exp.fulfill()
}
// Wait for the async request to complete
waitForExpectations(timeout: 40, handler: nil)
}
// Objective-C
- (void)setUp {
[super setUp];
NSString *description = [NSString stringWithFormat:#"%s%d", __FUNCTION__, __LINE__];
XCTestExpectation *exp = [self expectationWithDescription:description];
// Issue an async request
NSData *data = [self getData];
[db overwriteDatabaseData: data block: ^(){
[exp fulfill];
}];
// Wait for the async request to complete
[self waitForExpectationsWithTimeout:40 handler: nil];
}
There are two techniques for running asynchronous tests. XCTestExpectation and semaphores. In the case of doing something asynchronous in setUp, you should use the semaphore technique:
override func setUp() {
super.setUp()
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
let data = getData()
let semaphore = DispatchSemaphore(value: 0)
db.overwriteDatabase(data) {
// do some stuff
semaphore.signal()
}
semaphore.wait()
}
Note, for that to work, this onDone block cannot run on the main thread (or else you'll deadlock).
If this onDone block runs on the main queue, you can use run loops:
override func setUp() {
super.setUp()
var finished = false
// Fill out a database with data. I can make this call do anything, here
// it returns a block.
let data = getData()
db.overwriteDatabase(data) {
// do some stuff
finished = true
}
while !finished {
RunLoop.current.run(mode: .default, before: Date.distantFuture)
}
}
This is a very inefficient pattern, but depending upon how overwriteDatabase was implemented, it might be necessary
Note, only use this pattern if you know that onDone block runs on the main thread (otherwise you'll have to do some synchronization of finished variable).
Swift 4.2
Use this extension:
import XCTest
extension XCTestCase {
func wait(interval: TimeInterval = 0.1 , completion: #escaping (() -> Void)) {
let exp = expectation(description: "")
DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
completion()
exp.fulfill()
}
waitForExpectations(timeout: interval + 0.1) // add 0.1 for sure `asyncAfter` called
}
}
and usage like this:
func testShouldDeleteSection() {
let tableView = TableViewSpy()
sut.tableView = tableView
sut.sectionDidDelete(at: 0)
wait {
XCTAssert(tableView.isReloadDataCalled, "Check reload table view after section delete")
}
}
The example above isn't complete, but you can get the idea. Hope this helps.
Swift 5.5 & iOS 13+
You could overridefunc setUp() async throws for instance:
final class MyTestsAsync: XCTestCase {
var mockService: ServiceProtocolMock!
override func setUp() async throws {
mockService = await {
//... some async setup
}()
}
override func tearDown() async throws {
//...
See Apple docs on concurrency note here

Change default NSManagedObjectContext of NSPersistentDocument

Core data newbie here. I'm trying to change the default NSManagedObjectContext of an NSPersistentDocument, in order to initialise and use it with NSMainQueueConcurrencyType.
Currently I'm doing it in -windowControllerDidLoadNib: like this:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
NSManagedObjectContext *newMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[newMOC setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
[self setManagedObjectContext:newMOC];
}
This seemingly works fine. But I'm wondering if initialisation of the MOC in -windowControllerDidLoadNib: is the best thing to do, or whether it should be placed somewhere else and/or initialised in a different way.
Thanks for any help.
I'm experimenting with the Xcode template for a document-based CoreData app. The template creates an init() override which just calls super.init(). I want to run a large import in the background, so I added this to the document class:
class Document: NSPersistentDocument {
private var importQueue = DispatchQueue(label: "Importer")
override init() {
super.init()
let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
moc.mergePolicy = self.managedObjectContext!.mergePolicy
moc.persistentStoreCoordinator = self.managedObjectContext!.persistentStoreCoordinator
self.managedObjectContext = moc
}
func importStuff(url: URL) {
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.parent = self.managedObjectContext
var count = 0
moc.performAndWait {
...
count += 1
if count % 10000 == 0 {
do {
try moc.save()
moc.reset()
}
catch {
Swift.print("save failed at record #\(count): \(error.localizedDescription)")
}
}
return true
}
do {
try moc.save()
}
catch {
Swift.print("save failed at records #\(count): \(error.localizedDescription)")
}
}
Swift.print("imported \(count) records.")
}
#IBAction func import(_ sender: Any) {
...
importQueue.async {
self.importStuff(url: url)
}
}
}
This seems to work OK in my initial tests. I think initializing a new MOC in -windowControllerDidLoadNib: is OK, but if you have object controllers bound to the document MOC, they might perform a second fetch when the MOC is changed. Initializing it in the init will initialize it sooner, before the UI is loaded.
The preferred pattern is the "pass-the-baton" approach where you pass the managed object context down to the child view controllers. Give your controllers a managed object context attribute and simply pass it on when you present them.

Resources