简体   繁体   English

在 XCTest 中:如何测试 function 强制执行到主线程

[英]In XCTest: how to test that a function forced execution onto main thread

In the UI class I have a method that accesses UI elements, and hence is supposed to force itself onto a main thread.在 UI class 中,我有一个访问 UI 元素的方法,因此应该将其自身强制到主线程上。 Here's a minimal example of what I mean:这是我的意思的一个最小例子:

class SomeUI {

    func doWorkOnUI() {

        guard Thread.isMainThread else {

            DispatchQueue.main.async {

                self.doWorkOnUI()
            }
            return
        }

        print("Doing the work on UI and running on main thread")
    }
}

In the tests, of course there's no problem to test the case when doWorkOnUI() is already running on main thread.在测试中,当然测试doWorkOnUI()已经在主线程上运行的情况是没有问题的。 I just do this:我只是这样做:

func testWhenOnMainThread() {

    let testedObject = SomeUI()
    let expectation = XCTestExpectation(description: "Completed doWorkOnUI")

    DispatchQueue.main.async {
        testedObject.doWorkOnUI()
        expectation.fulfill()
    }

    wait(for: [expectation], timeout: 10.0)

    // Proceed to some validation
}

That is: force execution onto main thread.那就是:强制执行到主线程。 Wait for it to complete.等待它完成。 Do some checks.做一些检查。

But how to test the opposite case, ie ensure that function forced itself to run on main thread when called from the background thread?但是如何测试相反的情况,即确保 function 在从后台线程调用时强制自己在主线程上运行?

For example if I do something like:例如,如果我做类似的事情:

...
    DispatchQueue.global(qos: .background).async {
        testedObject.doWorkOnUI()
        expectation.fulfill()
    }
...

I just tested that function got executed from the background thread.我刚刚测试了 function后台线程执行。 But I didn't explicitly check that it ran on main thread.但是我没有明确检查它是否在主线程上运行。 Of course, since this function accesses UI elements, the expectation is that it crashes if not forced on main thread.当然,由于这个 function 访问 UI 元素,如果不在主线程上强制执行,预计它会崩溃。 So is "no crash" the only testable condition here?那么“没有崩溃”是这里唯一可测试的条件吗? Is there anything better?有更好的吗?

When there is an outer closure in the background and an inner closure on the main thread, we want two tests:当后台有外闭包,主线程有内闭包时,我们要两个测试:

  1. Call the outer closure.调用外部闭包。 Do a wait for expectations.等待期望。 Wait for 0.01 seconds.等待 0.01 秒。 Check that the expected work was performed.检查是否执行了预期的工作。
  2. Call the outer closure.调用外部闭包。 This time, don't wait for expectations.这一次,不要等待期望。 Check that the work was not performed.检查工作是否未执行。

To use this pattern, I think you'll have to change your code so that the tests can call the outer closure directly without having to do an async dance already.要使用此模式,我认为您必须更改代码,以便测试可以直接调用外部闭包,而不必已经执行异步舞蹈。 This suggests that your design is too deep to be testable without some changes.这表明您的设计太深,无法在不进行某些更改的情况下进行测试。

Find a way for an intermediate object to capture the closure.想办法让中间的 object 捕获闭包。 That is, instead of directly calling DispatchQueue.global(qos: .background).async , make a type that represents this action.也就是说,不是直接调用DispatchQueue.global(qos: .background).async ,而是创建一个表示此操作的类型。 Then a Test Spy version can capture the closure instead of dispatching it to the background, so that your tests can invoke it directly.然后 Test Spy 版本可以捕获闭包而不是将其分派到后台,以便您的测试可以直接调用它。 Then you can test the call back to main thread using async wait.然后您可以使用异步等待测试对主线程的回调。

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

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