警告 - 我读过关于测试线程的几个问题,但可能错过了答案,所以如果答案在那里并且我错过了,请指出我正确的方向。
我想测试在主队列上执行对reloadData的tableView调用。
这应该导致代码通过测试:
var cats = [Cat]() {
didSet {
DispatchQueue.main.async { [weak self] in
tableView.reloadData()
}
}
}
此代码应导致测试失败:
var cats = [Cat]() {
didSet {
tableView.reloadData()
}
}
测试应该是什么样的?
对测试人员的注意事项:我知道在运行应用程序时这是一件很容易捕获的事情,但当您重构和添加抽象层和多个网络时,这也很容易被忽略调用并希望用一些数据而不是其他数据等来更新UI ...所以请不要回答"对UI的更新进入主线程"我已经知道了。谢谢!
答案 0 :(得分:1)
使用 dispatch_queue_set_specific 功能,以便将键值对与main queue
然后使用 dispatch_queue_get_specific 检查是否存在密钥&值:
fileprivate let mainQueueKey = UnsafeMutablePointer<Void>.alloc(1)
fileprivate let mainQueueValue = UnsafeMutablePointer<Void>.alloc(1)
/* Associate a key-value pair with the Main Queue */
dispatch_queue_set_specific(
dispatch_get_main_queue(),
mainQueueKey,
mainQueueValue,
nil
)
func isMainQueue() -> Bool {
/* Checking for presence of key-value on current queue */
return (dispatch_get_specific(mainQueueKey) == mainQueueValue)
}
答案 1 :(得分:0)
我采取了更复杂的方法,将相关的Bool值添加到UITableView,然后调用UITableView来重定向reloadData()
fileprivate let reloadDataCalledOnMainThreadString = NSUUID().uuidString.cString(using: .utf8)!
fileprivate let reloadDataCalledOnMainThreadKey = UnsafeRawPointer(reloadDataCalledOnMainThreadString)
extension UITableView {
var reloadDataCalledOnMainThread: Bool? {
get {
let storedValue = objc_getAssociatedObject(self, reloadDataCalledOnMainThreadKey)
return storedValue as? Bool
}
set {
objc_setAssociatedObject(self, reloadDataCalledOnMainThreadKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
dynamic func _spyReloadData() {
reloadDataCalledOnMainThread = Thread.isMainThread
_spyReloadData()
}
//Then swizzle that with reloadData()
}
然后在测试中我更新了后台线程上的猫,所以我可以检查它们是否在主线程上重新加载。
func testReloadDataIsCalledWhenCatsAreUpdated() {
// Checks for presence of another associated property that's set in the swizzled reloadData method
let reloadedPredicate = NSPredicate { [controller] _,_ in
controller.tableView.reloadDataWasCalled
}
expectation(for: reloadedPredicate, evaluatedWith: [:], handler: nil)
// Appends on the background queue to simulate an asynchronous call
DispatchQueue.global(qos: .background).async { [weak controller] in
let cat = Cat(name: "Test", identifier: 1)
controller?.cats.append(cat)
}
// 2 seconds seems excessive but NSPredicates only evaluate once per second
waitForExpectations(timeout: 2, handler: nil)
XCTAssert(controller.tableView.reloadDataCalledOnMainThread!,
"Reload data should be called on the main thread when cats are updated on a background thread")
}