2016-06-21 4 views
11

У меня есть несколько модульных тестов, в которых я хотел бы проверить, вызван ли обратный вызов правильной очереди отправки.Проверьте правильность очереди отправки в Swift 3

В Swift 2 я сравнил метку текущей очереди с тестовой очередью. Однако в Swift 3 константа DISPATCH_CURRENT_QUEUE_LABEL больше не существует.

Я нашел функцию dispatch_assert_queue. Кажется, что мне нужно, но я не уверен, как это назвать.

Мой Swift 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") 

Update:

я запуталась отсутствием автозаполнения, но можно использовать __dispatch_assert_queue:

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

Несмотря на то, что это работает для модульных тестов, оно досадно останавливает весь процесс с помощью EXC_BAD_INSTRUCTION вместо того, чтобы просто не выполнить тест.

+1

Почему бы не установить в качестве предварительного условия для тестирования непосредственно в очереди или установить «конкретный» на нем и получить его позже? – KFDoom

+1

Не могли бы вы использовать setSpecific и getSpecific? src: https://github.com/duemunk/Async/blob/feature/Swift_3.0/AsyncTest/AsyncTests.swift – KFDoom

+0

@KFDoom Не знал об этих функциях. Это отлично подходит для этих модульных тестов! –

ответ

9

Отвечая на мой собственный вопрос:

на основе KFDoom's comments, я теперь с помощью setSpecific и getSpecific.

Это создает ключ, устанавливает его на очереди тест, а затем получает его снова:

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") 

Обратите внимание, что нет никакого значения, связанное с ключом (его тип Void), я м интересуется только наличием конкретной, а не ее стоимости.

Важно!
Обязательно сохраните ссылку на ключ или очистите его после его использования. В противном случае вновь созданный ключ может использовать один и тот же адрес памяти, что приводит к странному поведению. См.: http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue

3

Один из вариантов заключается в том, чтобы установить предварительное условие для проверки непосредственно для очереди или установки «конкретного» на нем и получения его позже. Кроме того, можно использовать setSpecific и getSpecific. В качестве альтернативы вы можете использовать проверку предварительного условия, если вы находитесь в очереди, чтобы она соответствовала потребностям «получить актуальность». ЦСИ:

и

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

+0

первая ссылка в данный момент отключена. – Sid

3

тесты, основанные на 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 (ключ:, value:)' не требуется во втором случае. – Lucien

13

Использование dispatchPrecondition(.onQueue(expectedQueue)), замена API Swift 3 для dispatch_assert_queue() C API.

Это было покрыто WWDC НОД сессии в этом году: https://developer.apple.com/videos/play/wwdc2016/720/

+0

Для этого требуется macOS 10.12/iOS 10.0, это правильно? - Может ли это использоваться в единичном тесте? –

+0

correct.It завершит процесс, если предварительное условие завершится неудачно, если вы можете справиться с этим в своем модульном тесте, он будет работать для этой цели – 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") 
} 
Смежные вопросы