在 Swift 中对异步函数进行单元测试

Unit testing an async function in Swift

提问人:John Citrowske 提问时间:9/27/2023 更新时间:9/27/2023 访问量:49

问:

我正在尝试在我的 swift 项目中测试异步函数,但我在测试的倒数第二行收到此错误 -> 线程 1:“在已失效的会话中创建的任务”wait(for: [expectation], timeout: 20.0)

我知道 API 可以工作并返回数据,因为它在运行项目和在 Postman 中工作。

这是单元测试

func testRswPrice(){
        //Given (Arrange)
        let fromLat = 26.336502
        let fromLon = -81.439354
        let toLat = 26.533842
        let toLon = -81.759150
        let vm = LocationSearchViewModel()
        
        //When (Act)
        let expectation = self.expectation(description: "Compute Price")
        vm.computePrice(fromLat, fromLon, toLat, toLon){ value in
            expectation.fulfill()
            XCTAssertEqual(value, 8000.0)
        }
        // Wait for the expectation to be fulfilled, or time out after a specified duration
        wait(for: [expectation], timeout: 20.0)
    }

这是 vm 内部的功能

func computePrice(_ fromLat: Double, _ fromLon: Double,_ toLat: Double, _ toLon: Double, completion: @escaping (Double?) -> Void){
        let url = URL(string:"NotShowingMyUrlToStackOverflowButPretendItsHere")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        print("fromLat: \(fromLat)    fromLon: \(fromLon)   toLat: \(toLat)    toLon: \(toLon)")
        let body = [
            "from": [
                "latitude": fromLat,
                "longitude": fromLon
            ],
            "to": [
                "latitude": toLat,
                "longitude": toLon
            ]
        ]
        let jsonData = try? JSONSerialization.data(withJSONObject: body)
        request.httpBody = jsonData
        
        URLSession.shared.dataTask(with: request){ data, response, error in
            guard let data = data, error == nil,
                  (response as? HTTPURLResponse)?.statusCode == 200
            else{
                completion(nil)
                return
            }
            
            //Decode JSON response
            do {
                let decoder = JSONDecoder()
                let response1 = try decoder.decode(PriceResponse.self, from: data)
                DispatchQueue.main.async {
                    completion(response1.price)
                    self.screenDisabled = false
                }
            } catch {
                DispatchQueue.main.async {
                    self.showingInvalidAlert = true
                }
                print("THE ERROR IS \(error.localizedDescription)")
            }
            
        }.resume()
    }
Swift 单元 测试

评论

0赞 Joakim Danielson 9/27/2023
你永远不应该在单元测试中对外部 API 发出实际请求,而是模拟该部分。在线搜索“swift mock urlsession”,让您了解如何执行此操作。
0赞 John Citrowske 9/27/2023
@JoakimDanielson 为什么会这样?这是我个人服务器的应用程序的端点,我不需要担心太多的请求等。
0赞 Joakim Danielson 9/27/2023
保持单元测试的隔离和简化仍然是一种很好的做法。现在,您依赖于外部资源,并且实际上在一次测试中测试了许多内容。
0赞 John Citrowske 9/28/2023
我很感激你的反馈。即使这不是最佳实践,我仍然想创建这些测试。对我来说,这段代码和我的服务器正常工作很重要。
0赞 Joakim Danielson 9/28/2023
也许测试效果不佳,也许尝试不使用它只是为了看看这是否是问题所在和/或添加打印语句或调试以查看发生了什么。请注意,在捕获解码错误时会遇到一个错误,因为您当时没有调用完成处理程序。也许是时候转向异步/等待了......DispatchQueue.main.async

答: 暂无答案