繁体   English   中英

XCTest - 无法理解/实现单元测试中的期望(测试 aysnc 代码)

[英]XCTest - Unable to understand / implement expectations in unit tests (to test aysnc code)

(注意 - 我正在为 macOS 开发,所以请......特定于 iOS 的建议对我没有帮助)

我正在尝试做的事情:我有一个应用程序组件,它在后台线程上执行一个简短的任务,然后,如果满足某些条件,则在主线程上异步发送一个通知。

注意 - 我没有在我的应用程序代码中使用 NSNotification 我正在使用自己的自定义通知机制。 因此,任何与 NSNotification 相关的解决方案都不适用于我。

我正在为上述应用程序组件编写单元测试,只是想检查该通知是否确实已发送 在执行断言之前,我的测试必须能够等待一秒钟左右才能让通知时间到达其订阅者/观察者。

我希望能够在我的测试中测试这两种可能的情况:两者都是正常情况。

  • 已发送通知。

  • 未发送通知。

在阅读了几个文档和代码示例数小时后,我不明白如何实现这一目标。

我只想在测试中等待一秒钟。 真的有这么复杂吗?

  • 睡眠()不起作用
  • DispatchQueue.main.asyncAfter(time) 不起作用
  • 定时器不工作

这是需要测试的应用程序组件及其单元测试:

在下面的代码中,我在哪里放置expectation.fulfill()???

class ComponentBeingTested {

    func methodBeingTested() {

        doSomeWork()
        if certainConditionsAreMet {
             DispatchQueue.main.async {sendOutNotification()}
        }
    }
}

...

class UnitTestForComponentBeingTested: XCTestCase {

    let objectBeingTested = ComponentBeingTested()

    func testMethodBeingTested() {

          let expectation = self.expectation(description: "Notification was sent")

          // Call the code being tested
          objectBeingTested.methodBeingTested()

          // How do I do this with expectations ??? Where does expectation.fulfill() go ?
          waitForOneSecond()

          XCTAssertTrue(notificationSent)      // Assume the value of notificationSent is available

    }
}

这是一种方法

func testMethodBeingTested() {

      // create expectation
      let expectation = self.expectation(description: "Notification was sent")

      // set expectation condition
      var notificationSent = false
      let observer = NotificationCenter.default
            .addObserver(forName: _Your_Notification_Name, object: nil, queue: nil) { _ in
            notificationSent = true
            expectation.fulfill()
        }

      // Call the code being tested
      objectBeingTested.methodBeingTested()

      // wait for expectation
      self.wait(for: [expectation], timeout: 5)

      XCTAssertTrue(notificationSent)
}

查看XCTNSNotificationExpectation ,它会在发布匹配通知时实现。 可以使用不同的初始化程序,具体取决于您希望对实现期望的限制程度。

要检查通知是否发送,请将预期 object 上的isInverted设置为true

然后只需在测试结束时添加对waitForExpectations(timeout:handler:)的调用。

好的,经过大量的试验和错误,这对我很有用:

描述:我基本上在我的测试用例 class 中创建了一个助手 function ,其中包含所有样板期望/等待代码。 它执行以下操作:

1 - 创建一个期望(即 XCTestExpectation)作为一种形式。

2 - 在预期的延迟期之后,在某个全局队列线程上调用我的(任意)测试用例断言代码(作为闭包传入)。 一旦这个断言代码完成,期望就实现了(再次,形式)。

3 - 通过调用 XCTestCase.wait(timeout) 等待期望。 这确保了当我的断言代码在另一个线程上完成时主线程/运行循环保持活动状态。

然后,在我的测试用例中,我简单地调用该帮助程序 function,为其提供等待时间和一些要执行的代码(即我的断言)。

这样,我就有了一个简单而富有表现力的可重用 function,它隐藏了我一开始就认为没有必要的所有过度丑陋的期望。

我可以把这个助手放在一个基本的 class 中,比如 MyAppTestCase: XCTestCase,这样我的所有测试用例类都可以使用它。

注意 - 这个解决方案可以得到增强,甚至更通用/可重用,但到目前为止,这对于最初发布的问题来说已经足够了。

解决方案

class ComponentBeingTested {

    func methodBeingTested() {

        doSomeWork()
        if certainConditionsAreMet {
             DispatchQueue.main.async {sendOutNotification()}
        }
    }
}

...

class UnitTestForComponentBeingTested: XCTestCase {

    let objectBeingTested = ComponentBeingTested()

    // Helper function that uses expectation/wait to execute arbitrary
    // test code (passed in as a closure) after some delay period.
    func executeAfter(_ timeSeconds: Double, _ work: (@escaping () -> Void)) {

        let theExpectation = expectation(description: "some expectation")

        // Execute work() after timeSeconds seconds
        DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + timeSeconds) {

            // The call to work() will execute my test assertions
            work()

            // As a formality, fulfill the expectation
            theExpectation.fulfill()
        }

        // Wait for (timeSeconds + 1) seconds to give the work() call 
        // some time to perform the assertions
        wait(for: [theExpectation], timeout: timeSeconds + 1)
    }

    func testMethodBeingTested() {

          // Call the code being tested
          objectBeingTested.methodBeingTested()

          // Call the helper function above, to do the waiting before executing
          // the assertions
          executeAfter(0.5) {

              // Assume the value of notificationSent is computed elsewhere
              // and is available to assert at this point
              XCTAssertTrue(notificationSent)      
          }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM