ios - 调用异步方法的方法的单元测试

标签 ios objective-c swift unit-testing testing

假设我有这些代码行:

func reset() {
    initializeAnythingElse() {
        // AnythingElse
    }

    initializeHomeData() {
        // HomeData
    }
}

func initializeHomeData(callback: @escaping (()-> Void)) {
    getHomeConfig() {
        callback()
    }
}

func initializeAnythingElse(callback: @escaping (()-> Void)) {
    getAnythingElse() {
        callback()
    }
}

我想为该代码编写一个单元测试。对于 initializeHomeDatainitializeAnythingElse,我可以像这样编写单元测试:

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/

相关文章:

ios - 当 transform.rotation.y > M_PI 时,UIView 的图层旋转不会将背景保持在顶部

ios - 如何选择何时返回 Collection View 单元格

swift - 倾斜矩形 UIBezierPath 的一条边

ios - 清除 VNDocumentCameraScan Swift 中的扫描图像缓存

ios - 为什么在IOS8中使用coreBluetooth connectPeripheral没有调用delegate方法

ios - 界面生成器 : Why is my UIView transparent?

ios - 忘记密码代码 : Objective C

ios - Objective-c iOs 退出 View 时自动保存信息

ios - tableView 和释放实例的问题

ios - 从不正确的线程 Swift 访问的 Realm