【问题标题】:Write unit test for function that uses URLSession and RxSwift为使用 URLSession 和 RxSwift 的函数编写单元测试
【发布时间】:2020-03-01 21:13:09
【问题描述】:

我有一个创建和返回 Observable 的函数,它使用 URLSession 下载和解码数据。我想为此功能编写单元测试,但不知道如何解决。

功能:

func getRecipes(query: String, _ needsMoreData: Bool) -> Observable<[Recipes]> {

        guard let url = URL(string: "https://api.spoonacular.com/recipes/search?\(query)&apiKey=myApiKey") else {
            return Observable.just([])
        }

        return Observable.create { observer in
            let task = URLSession.shared.dataTask(with: url) { (data, response, error) in

                guard let data = data else {
                    return
                }

                do {
                    if self.recipes == nil {
                        self.recipes = try self.decoder.decode(Recipes.self, from: data)
                        self.dataList = self.recipes.results
                        self.baseUrl = self.recipes.baseUrl
                    } else {
                        if needsMoreData {
                            self.recipes = try self.decoder.decode(Recipes.self, from: data)
                            self.dataList.append(contentsOf: self.recipes.results.suffix(50))
                        } else {
                            self.dataList = try self.decoder.decode(Recipes.self, from: data).results
                        }
                    }
                    observer.onCompleted()
                } catch let error {
                    observer.onError(error)
                }
            }
            task.resume()

            return Disposables.create {
                task.cancel()
            }
        }
        .trackActivity(activityIndicator)
    }

【问题讨论】:

  • 嗯,你想测试什么?测试URLSession和dataTask没有意义;你知道那些做什么,并且在测试期间上网通常是没有意义的(除非你的目标是测试你的 api 仍在运行并且没有改变它的输出)。所以你想mock URLSession 接口。我不知道 Observable,但我知道如何模拟 URLSession 行为;您创建一个 URLProtocol 子类。现在请求通过您的代码,您可以将任何您想要的内容返回到数据任务的完成处理程序中。

标签: swift unit-testing rx-swift urlsession


【解决方案1】:

显而易见的答案是注入 dataTask 而不是在函数中使用单例。像这样的:

func getRecipes(query: String, _ needsMoreData: Bool, dataTask: @escaping (URL, @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask) -> Observable<[Recipes]> {

    guard let url = URL(string: "https://api.spoonacular.com/recipes/search?\(query)&apiKey=myApiKey") else {
        return Observable.just([])
    }

    return Observable.create { observer in
        let task = dataTask(url) { (data, response, error) in
// and so on...

你可以在主代码中这样调用它:

getRecipes(query: "", false, dataTask: URLSession.shared.dataTask(with:completionHandler:))

在你的测试中,你需要这样的东西:

func fakeDataTask(_ url: URL, _ completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
    XCTAssertEqual(url, expectedURL)
    completionHandler(testData, nil, nil)
    return URLSessionDataTask()
}

let result = getRecipes(query: "", false, dataTask: fakeDataTask)

你知道 URLSession 已经为它创建了响应式扩展吗?我最喜欢的一个是:URLSession.shared.rx.data(request:),它返回一个 Observable,如果获取数据有任何问题,它将发出错误。我建议你使用它。

【讨论】:

  • 我其实不知道,谢谢你的提示,我会尝试重新实现它。
  • 嘿,我尝试按照所说的方式实现它,但是在 ViewController 中调用函数时遇到问题。如果您可以向我展示一个调用它的示例,这会是一个问题吗?
  • 我编辑了问题以包含如何调用函数的示例。
  • 你应该观看这个视频,它在示例中使用了 Python,但关于 Swift 也可以这样说:youtube.com/watch?v=DJtef410XaM
猜你喜欢
  • 2018-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 2021-06-12
  • 2019-01-12
相关资源
最近更新 更多