2016-06-21 22 views
11

Ho alcuni test di unità in cui vorrei verificare se una richiamata viene chiamata sulla coda di invio corretta.Controllare se sulla coda di invio corretta in Swift 3

In Swift 2, ho confrontato l'etichetta della coda corrente con la mia coda di test. Tuttavia in Swift 3 la costante DISPATCH_CURRENT_QUEUE_LABEL non esiste più.

Ho trovato la funzione dispatch_assert_queue. Che sembra essere quello di cui ho bisogno, ma non sono sicuro di come chiamarlo.

mio Swift Codice 2:

let testQueueLabel = "com.example.my-test-queue" 
let testQueue = dispatch_queue_create(testQueueLabel, nil) 

let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))! 
XCTAssertEqual(currentQueueLabel, testQueueLabel, "callback should be called on specified queue") 

Aggiornamento:

mi sono confuso dalla mancanza di completamento automatico, ma è possibile utilizzare __dispatch_assert_queue:

if #available(iOS 10.0, *) { 
    __dispatch_assert_queue(test1Queue) 
} 

Mentre funziona per i test unitari, interrompe fastidiosamente l'intero processo con un EXC_BAD_INSTRUCTION invece di fallire un test.

+1

perché non impostare una precondizione per testare direttamente per la coda o impostare "specifiche" su di esso e recuperare in un secondo momento? – KFDoom

+1

Non potresti usare setSpecific e getSpecific? src: https://github.com/duemunk/Async/blob/feature/Swift_3.0/AsyncTest/AsyncTests.swift – KFDoom

+0

@KFDoom Non conoscevo queste funzioni. Funziona benissimo per questi test unitari! –

risposta

9

Rispondendo alla mia domanda:

Sulla base di KFDoom's comments, Ora sto utilizzando setSpecific e getSpecific.

Questo crea una chiave, la mette sulla coda di prova, e più tardi, ottiene di nuovo:

let testQueueLabel = "com.example.my-test-queue" 
let testQueue = DispatchQueue(label: testQueueLabel, attributes: []) 
let testQueueKey = DispatchSpecificKey<Void>() 

testQueue.setSpecific(key: testQueueKey, value:()) 

// ... later on, to test: 

XCTAssertNotNil(DispatchQueue.getSpecific(key: testQueueKey), "callback should be called on specified queue") 

Nota che non c'è alcun valore associato alla chiave (il suo tipo è Void), I' m interessato solo all'esistenza dello specifico, non al suo valore.

Importante!
Assicurarsi di mantenere un riferimento alla chiave o di pulirlo dopo aver finito di usarlo. Altrimenti una chiave appena creata potrebbe utilizzare lo stesso indirizzo di memoria, portando a comportamenti strani. Vedi: http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue

3

Un'opzione è impostare una condizione preliminare per testare direttamente la coda o impostare "specifico" su di esso e recuperarla in un secondo momento. Inoltre, si potrebbe usare setSpecific e getSpecific. In alternativa, è possibile utilizzare un controllo di precondizione se si è in coda in modo da soddisfare la necessità di "ottenere corrente". src:

e

https://github.com/apple/swift/blob/master/stdlib/public/SDK/Dispatch/Dispatch.swift

+0

il primo link è ora rotto. – Sid

3

Test basati su KFDoom's answer:

import XCTest 
import Dispatch 

class TestQueue: XCTestCase { 

    func testWithSpecificKey() { 
     let queue = DispatchQueue(label: "label") 

     let key = DispatchSpecificKey<Void>() 
     queue.setSpecific(key:key, value:()) 

     let expectation1 = expectation(withDescription: "main") 
     let expectation2 = expectation(withDescription: "queue") 

     DispatchQueue.main.async { 
      if (DispatchQueue.getSpecific(key: key) == nil) { 
       expectation1.fulfill() 
      } 
     } 

     queue.async { 
      if (DispatchQueue.getSpecific(key: key) != nil) { 
       expectation2.fulfill() 
      } 
     } 

     waitForExpectations(withTimeout: 1, handler: nil) 
    } 

    func testWithPrecondition() { 
     let queue = DispatchQueue(label: "label") 

     let expectation1 = expectation(withDescription: "main") 
     let expectation2 = expectation(withDescription: "queue") 

     DispatchQueue.main.async { 
      dispatchPrecondition(condition: .notOnQueue(queue)) 
      expectation1.fulfill() 
     } 

     queue.async { 
      dispatchPrecondition(condition: .onQueue(queue)) 
      expectation2.fulfill() 
     } 

     waitForExpectations(withTimeout: 1, handler: nil) 
    } 

} 
+0

'setSpecific (chiave:, valore:)' non è necessario nel secondo caso. – Lucien

13

Uso dispatchPrecondition(.onQueue(expectedQueue)), la sostituzione Swift 3 della API dispatch_assert_queue() C.

Questo era coperto in seduta WWDC GCD di quest'anno: https://developer.apple.com/videos/play/wwdc2016/720/

+0

Questo richiede macOS 10.12/iOS 10.0, è corretto? - Può essere usato in un test unitario? –

+0

correct.It terminerà il processo se la precondizione fallisce, a patto che tu possa gestirla nel test dell'unità funzionerà per quello scopo – das

-1
/* 
Dispatch queue and NSOperations in Swift 3 Xcode 8 
*/ 

protocol Container { 

    associatedtype ItemType 
    var count: Int { get } 
    mutating func pop() 
    mutating func push(item: ItemType) 
    mutating func append(item: ItemType) 
    subscript(i: Int) -> ItemType { get } 
} 

//Generic Function 
struct GenericStack<Element> : Container { 
    mutating internal func push(item: Element) { 
     items.append(item) 
    } 

    mutating internal func pop() { 
     items.removeLast() 
    } 

    var items = [ItemType]() 
    internal subscript(i: Int) -> Element { 
     return items[i] 
    } 

    mutating internal func append(item: Element) { 
     self.push(item: item) 
    } 

    internal var count: Int { return items.count } 
    typealias ItemType = Element 
} 

var myGenericStack = GenericStack<String>() 
myGenericStack.append(item: "Narendra") 
myGenericStack.append(item: "Bade") 
myGenericStack.count 
myGenericStack.pop() 
myGenericStack.count 

//Some NSOperation 
class ExploreOperationAndThread { 

    func performOperation() { 

     //Create queue 
     let queue = OperationQueue() 
     let operation1 = BlockOperation { 
      var count = myGenericStack.count 
      while count > 0 { 
       myGenericStack.pop() 
       count -= 1 
      } 
     } 

     operation1.completionBlock = { 
      print("Operation 1") 
     } 

     let operation2 = BlockOperation { 
      var count = 0 
      while count == 10 { 
       myGenericStack.append(item: "ItemAdded") 
       count += 1 
      } 
     } 

     operation2.completionBlock = { 
      print("Operation 2") 
      print(myGenericStack.items) 
     } 

     //Suppose operation 3 is related to UI 

     let operation3 = BlockOperation { 
      //run on main thread 
      DispatchQueue.main.async { 
       print(myGenericStack.items.count) 
      } 
     } 

     operation3.completionBlock = { 
      print("Operation 3") 
      print(myGenericStack.items.count) 
     } 
     //add operation into queue 
     queue.addOperation(operation3) 
     queue.addOperation(operation1) 
     queue.addOperation(operation2) 
     //Limit number of concurrent operation in queue 
     queue.maxConcurrentOperationCount = 1 
     //add dependancies 
     operation1.addDependency(operation2) 
     operation2.addDependency(operation3) 

     if myGenericStack.items.count == 0 { 
      //remove dependency 
      operation1.removeDependency(operation2) 
     } 
    } 
} 

//Other ways of using queues 
DispatchQueue.global(qos: .userInitiated).async { 
    ExploreOperationAndThread().performOperation() 
} 

DispatchQueue.main.async { 
    print("I am performing operation on main theread asynchronously") 
} 

OperationQueue.main.addOperation { 
    var count = 0 
    while count == 10 { 
     myGenericStack.append(item: "Narendra") 
     count += 1 
    } 
} 

DispatchQueue.main.asyncAfter(deadline: .now() + 1.5 , execute: { 
    ExploreOperationAndThread().performOperation() 
}) 

let queue2 = DispatchQueue(label: "queue2") //Default is serial queue 
queue2.async { 
    print("asynchronously") 
}