为使用 URLSession 和 RxSwift 的函数编写单元测试
Write unit test for function that uses URLSession and RxSwift
我有一个函数可以创建 returns 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)
}
显而易见的答案是注入 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 已经为它创建了 Reactive 扩展吗?我最喜欢的是:URLSession.shared.rx.data(request:)
其中 returns 一个 Observable,如果在获取数据时出现任何问题,它会发出错误。我建议你使用它。
我有一个函数可以创建 returns 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)
}
显而易见的答案是注入 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 已经为它创建了 Reactive 扩展吗?我最喜欢的是:URLSession.shared.rx.data(request:)
其中 returns 一个 Observable,如果在获取数据时出现任何问题,它会发出错误。我建议你使用它。