假设我有这些代码行:
func reset() {
initializeAnythingElse() {
// AnythingElse
}
initializeHomeData() {
// HomeData
}
}
func initializeHomeData(callback: @escaping (()-> Void)) {
getHomeConfig() {
callback()
}
}
func initializeAnythingElse(callback: @escaping (()-> Void)) {
getAnythingElse() {
callback()
}
}
我想为该代码编写一个单元测试。对于 initializeHomeData
和 initializeAnythingElse
,我可以像这样编写单元测试:
func testInitializeHomeData() {
let successExpectation = expectation(description: "")
sut.initializeHomeData {
successExpectation.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
// Validation goes here
}
func testInitializeAnythingElse() {
let successExpectation = expectation(description: "")
sut.initializeAnythingElse {
successExpectation.fulfill()
}
waitForExpectations(timeout: 1.0, handler: nil)
// Validation goes here
}
我的问题是,如何测试 reset()
? 我是否应该在 testReset()
中调用它们,例如:
func testReset() {
testInitializeHomeData()
testInitializeAnythingElse()
}
但我认为这不是正确的实现方式。
最佳答案
你是对的。要测试 reset
,您需要调用 reset
,而不是它的内部方法。
也就是说,reset
目前的编写方式使其无法测试。您能够如此轻松地测试其他独立方法的原因是因为 callback
参数都接受。
我建议您重写 reset 以允许两个可选的回调,如下所示:
typealias Callback = () -> ()
func reset(
homeDataCallback: @escaping Callback? = nil,
anythingElseCallback: @escaping Callback? = nil) {
initializeAnythingElse() {
anythingElseCallback?()
}
initializeHomeData() {
homeDataCallback?()
}
}
请注意,此更改允许您在这两个内部调用完成时以异步方式收到通知。
现在,您的测试方法需要在编写时考虑到某种同步原语,因为从逻辑上讲,只有当家庭数据和其他任何事情都完成并调用它们的回调时,重置才会完成。
有很多方法可以实现这一点,但我将向您展示一种使用信号量的方法:
func testReset() {
let expectation = expectation(description: "reset() completes within some duration")
// some mechanism to synchronize concurrent tasks
// I am using a semaphore
let s = DispatchSemaphore(value: 0)
let homeCallback: Callback = {
s.signal() // signals the completion of home data init
}
let anythingElseCallback: Callback = {
s.signal() // signals the completions of anything else setup
}
// call your reset method as part of the test
reset(homeDataCallback: homeCallback, anythingElseCallback: anythingElseCallback)
// we know we need to wait for two things to complete
// init home data and anything else, so do that
s.wait()
s.wait()
// at this step, reset's internal async methods
// have completed so we can now
// fulfill the expectation
expectation.fulfill()
}
请注意,所有这些更改都是为了让您能够测试 reset
调用。您的函数签名允许您在现有代码中将 reset()
编写为当前代码,因为它具有可选参数,这些参数均设置为 nil 作为默认值。
关于ios - 调用异步方法的方法的单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45135623/