Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit test for `downloadTask` with `completionHandler `?

Here is my downloading function:

// Download a file from the url to the local directory
class func downloadUrl(url: URL, to dirUrl: URL, completion: (() -> ())?){
    let sessionConfig = URLSessionConfiguration.default
    let session = URLSession(configuration: sessionConfig)
    let request = URLRequest(url: url)

    let task = session.downloadTask(with: request) {(tempLocalUrl, response, error) in
        if let tempLocalUrl = tempLocalUrl, error == nil {
            // Success, copy the downloaded file from the memory to the disk
            print("Finished downloading!")
            do {
                try FileManager.default.copyItem(at: tempLocalUrl,to:
                    dirUrl.appendingPathComponent((response?.suggestedFilename)!))
                if completion != nil {
                    completion!()
                }
            } catch (let writeError) {
                print("Fliled to write file \(dirUrl) : \(writeError)")
            }
        } else {
            print("Failure: \(String(describing: error?.localizedDescription))")
        }
    }
    task.resume()
}

I want to write a unit test method to test whether it downloads the file from url to the dirUrl.

func testDownloadUrl(){
    let fm = FileManager.default
    let url = URL(string: "https://raw.githubusercontent.com/apple/swift/master/README.md")
    fileDownloader.downloadUrl(url: url!, to: fm.temporaryDirectory, completion: nil)

    // Check the contents of temp file
    let tempContents = try? fm.contentsOfDirectory(atPath: fm.temporaryDirectory.path)
        print("Contents: \(tempContents)")
    }

However, there was no output "Finished downloading!" or "Failure ..." even though I passed the unit test, so I guess the completionHandler was not called in this test case.

My question is how to make the unit test method wait until the download task completes?

like image 406
Jay Wong Avatar asked Oct 11 '25 07:10

Jay Wong


1 Answers

The standard idiom is to use Xcode asynchronous testing APIs, mostly the XCTestExpectation class. Those are available since Xcode 6 so, if you are using a recent version, you should be covered ;)

The general async test template is as follows:

func testAsyncFunction() {
    let asyncDone = expectation(description: "Async function")
    ...
    someAsyncFunction(...) {
        ...
        asyncDone.fulfill()
    }
    wait(for: [asyncDone], timeout: 10)
    /* Test the results here */
}

This will block your test execution until the aforementioned function completes (or the specified timeout has elapsed).

like image 193
Paulo Mattos Avatar answered Oct 14 '25 06:10

Paulo Mattos