强制 Alamofire 在主线程上做所有事情?

Posted

技术标签:

【中文标题】强制 Alamofire 在主线程上做所有事情?【英文标题】:Force Alamofire to do everything on main thread? 【发布时间】:2020-03-24 15:05:05 【问题描述】:

有没有办法强制 Alamofire 在主线程上完成所有工作?这是一个测试场景,我需要主线程在网络调用完成并且我知道状态之前不继续执行。有问题的测试曾经在XCTest 框架下运行,所以我会使用XCTestExpectation 等待完成。我使用的环境不适用于XCTest,所以我需要 Alamofire 同步。


由于前 2 个 cmets 都建议我应该如何测试,让我提供更多背景信息。

我有大约 100 个测试在过去 2 年中运行良好。这些测试不是单元测试,而是使用 Google 的 EarlGrey 1 进行的 UI 测试。EarlGrey 1 提供了在 XCTest 下运行的 UI 测试,因此您可以将 UI 测试与应用程序状态的白盒检查结合起来。太好了。

Earl Grey 1 不支持 ios 13,因此我将测试移植到 EarlGrey 2,它在 XCUITest 之上运行。对于白盒方面,您可以从测试进程远程调用注入到您的应用中的包。

在这种情况下,“模拟”是服务器,但它确实会进行网络调用,并且当我调用测试包时会发生这些情况。我需要知道他们什么时候完成,这样我才能在他们完成后返回。

【问题讨论】:

我会质疑这个问题的整个前提。无需测试 Alamofire;你知道它的作用。而且几乎没有任何理由让 Internet 参与测试(除非偶尔确保 API/服务器仍然存在)。您应该模拟互联网交互并提供已知数据来测试您的代码。 另请注意,希望 Alamofire 在主线程上响应和希望 Alamofire 同步是完全不同的两件事。 【参考方案1】:

回答您的问题:没有适当的方法可以强制 Alamofire 使用 Alamorefire 本身在主线程上进行调用。 一般来说,您可以使用信号量使线程等待结果。 Alamofire 似乎在主线程上响应,因此可能会导致死锁。

你可以让线程休眠直到回调被处理:

var result: Data?
var done = false

// Do a call which will finish asynchonously
someCall(withBlock:  someResult in
    // Once done,
    result = someResult
    done = true
)

// Make the main thread wait
while(!done) 
    Thread.sleep(forTimeInterval: 0.1)


// This will be called after the block has been completed
print("\(result)")

同样可以应用于 Alamofire 调用,请注意这会导致线程相互等待的死锁。

但是为了帮助您解决问题。理想情况下,您不会因为多种原因等待在测试中执行网络调用,包括:

测试应仅涵盖单个功能,网络调用超出此范围。 包含网络调用意味着没有互联网连接等外部因素会影响测试结果,尽管它对您的代码按预期执行几乎没有影响。

一种常见的方法是“模拟”应该被视为超出测试范围的部分。 Here's a post about doing this for Alamofire.

【讨论】:

@matt - 你说的是根本不回答我的问题,而是简单地告诉我为什么我的问题无效以及我应该如何以不同的方式做。这就是为什么我咬了一口。 :-) 凯文,谢谢。我实际上尝试了您提供的代码。问题在于,Alamofire 似乎在后台线程上“工作”,但在主线程上执行块。结果,主线程在等待调用上被阻塞。顺便说一句,我已经添加了更多关于我正在做什么的细节。这些不是单元测试,因此我无法像在单元测试中那样真正测试单个功能。 @markand 嗯,我认为 Alamofire 的默认响应是在主线程上给出的。用不同的方法更新了答案,虽然它看起来很hacky :) @KevinR - 这很老套,但可能就是这样!并且在主线程上给出了响应。如果有办法改变它,那也可以这样做,因为我可以从后台线程发出信号量。我会试试看。谢谢!

以上是关于强制 Alamofire 在主线程上做所有事情?的主要内容,如果未能解决你的问题,请参考以下文章

Alamofire - 有没有办法设置自定义默认队列来处理所有请求回调

SceneKit - 线程 - 在哪个线程上做啥?

Alamofire RequestRetrier,在主操作队列上调用完成块可以吗?

在主线程的密集功能期间 UI 未更新(在 Swift 中)

在服务中使用 AsyncTask

-[GCKCastDeviceProvider stopDiscovery] 必须在主线程上调用