我正在尝试对一个定制的UIView
进行单元测试,它会异步地更改UI。
import UIKit
class DemoView: UIView {
var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
label = UILabel(frame: .zero)
self.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
self.centerXAnchor.constraint(equalTo: label.centerXAnchor).isActive = true
self.centerYAnchor.constraint(equalTo: label.centerYAnchor).isActive = true
}
@MainActor
func setLabel(_ text: String) {
Task {
try await Task.sleep(for: .milliseconds(100))
label.text = text
}
}
}
我想测试一下,在调用setLabel(_:)
函数后,标签上的文本确实发生了变化,因此我编写了以下测试:
@MainActor
func testExample() async throws {
let demoView = DemoView(frame: .zero)
XCTAssertEqual(demoView.label.text, nil)
demoView.setLabel("New Text")
let expectLabelChange = expectation(for: NSPredicate(block: { _, _ in
demoView.label.text != nil
}), evaluatedWith: demoView.label)
await waitForExpectations(timeout: 5)
XCTAssertEqual(demoView.label.text, "New Text")
}
但是异常运行到超时,Assert失败。当我设置断点时,我可以看到setLabel(_:)
中的Task
被执行,但在休眠后从未重新进入,即使超时足够长。只有在waitForExpectations
完成后,setLabel(_:)
中的任务才继续,但这对Assert捕捉更改来说太晚了。
如何编写测试,以便setLabel(_:)
中的Task
继续?
**注意:**代码是为了演示这个问题而调整的。在真实的的应用程序中,我调用了一个API而不是休眠。
1条答案
按热度按时间oymdgrw71#
你不需要
async
测试,也不应该使用它。setLabel
不是async
,你使用的是一个期望!这个测试会通过(我在这个过程中重写了一些小东西):更好的是,从
setLabel
调用中删除@MainActor
;然后你也可以从测试函数中删除它。在什么情况下
async
适合测试?如果setLabel
是async
!假设您将setLabel
重写为:async
--现在你不需要期望了!看看一切变得多么简单: