Написать модульный тест для функции, которая использует URLSession и RxSwift - PullRequest
1 голос
/ 29 февраля 2020

У меня есть функция, которая создает и возвращает Observable, который загружает и декодирует данные, используя URLSession. Я хотел написать модульный тест для этой функции, но понятия не имел, как ее решить.

function:

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)
    }

1 Ответ

1 голос
/ 01 марта 2020

Очевидный ответ - ввести 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, который выдаст ошибку, если возникнут проблемы с получением данных. Я предлагаю вам использовать его.

...