I have a function in which i am calling a network call as,
func newtorkCall() {
//some other code
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response:
NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if (data != nil) {
//save the data in a variable temp
}
})
}
this is working fine and i am getting the data.
But i want to mock this NSURLConnection.sendAsynchronousRequest in unit testing so that i can hardcode the data and pass it to the variable temp in the source code and test it.
How can I do it?
Related
This question already has answers here:
Run code only after asynchronous function finishes executing
(2 answers)
Closed 6 years ago.
I have an asynchronus request in a class like this,
class Req{
func processRequest(){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
//Do some operation here
})
}
}
And I am calling this method from one view controller like this,
class classOne{
func classOneFun(){
Req().processRequest()
self.demoFun()
}
func demoFun(){
//Do some operation here
}
}
And I am calling the same function from another view controller like this,
class classTwo{
func classTwoFun(){
Req().processRequest()
self.sampleFun()
}
func sampleFun(){
//Do some operation here
}
}
Now I want to call the demoFun() or sampleFun() only after the processRequest() is completed. If demoFun() or sampleFun() and processRequest() are in the same class, then I can call the functions in the completion handler of processRequest(). But, in my case I cannot do that, since I have nearly 20 view controllers that call the processRequest() function. I cannot use SynchronusRequest as it is deprecated in Swift 2.0. So how can I call the other functions once the asynchronus request is completed?
Create blocks
class Req{
func processRequest(success: () -> ()){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
//Do some operation here
success()
})
}
}
class classOne{
func classOneFun(){
Req().processRequest { () -> () in
self.demoFun()
}
}
func demoFun(){
//Do some operation here
}
}
You will need to modify your processRequest function to receive a closure. For example:
class Req{
func processRequest(callback: Void ->Void){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
//Do some operation here
//Call the callback here:
callback()
})
}
}
Now wherever, you want to call the processRequest API, you can add the code you want to execute soon after your asynchronous callback processing. For example:
class classOne{
func classOneFun(){
Req().processRequest(){ //Passing the closure as a trailing closure with the code that we want to execute after asynchronous processing in processRequest
self.demoFun()
}
}
func demoFun(){
//Do some operation here
}
}
HTH.
I'm trying to write failing tests for my network calls but can't find anyway to access the connection settings from within my tests.
This code works fine for testing a success case:
func testRetrieveProducts() {
let expectation = expectationWithDescription("asynchronous request")
Requests().retrieveProducts({ (products) -> () in
// check that we have two arrays returned.
XCTAssert(products.count == 2)
expectation.fulfill()
}) { (error) -> () in
XCTFail("Request failed")
}
waitForExpectationsWithTimeout(5.0, handler: nil)
}
But I've been looking for a way to test network timeouts, and incorrect data being returned.
I can probably test incorrect data by calling functions individually inside the retrieveProducts function and stubbing stuff out, but doing something as simple as turning off the internet is proving to be quite difficult.
I know we have access to the Network Link Conditioner, but turning this on and off for each test isn't an option.
I'd love to have access to something as simple as:
func testFailRetrieveProducts() {
let expectation = expectationWithDescription("asynchronous request")
SomeNetworkClass.disableInternet()
Requests().retrieveProducts({ (products) -> () in
}) { (error) -> () in
XCTAssert(error == SomeError.TimeoutError)
}
waitForExpectationsWithTimeout(5.0, handler: nil)
}
Any solutions out there that can handle what I'm after, or am I going about this all wrong?
Take a look at this NSHipster article about Apple's Network Link Conditioner. There's a lot of presets and you can create your own custom network profile. Unfortunately there doesn't seem to be a way to throttle the network in code.
A somewhat viable alternative however is to use ReactiveCocoa and model all your network events as SignalProducers. Then you can use the throttle or wait function, depending on your intentions.
I ended up just mocking the network calls, which to be honest is a lot better than performing tests over an actual connection, as these can be very unreliable anyway.
Here's my mock NSURLSession
class MockSession: NSURLSession {
var completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?
static var mockResponse: (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)
override class func sharedSession() -> NSURLSession {
return MockSession()
}
override func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask {
self.completionHandler = completionHandler
return MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
}
class MockTask: NSURLSessionDataTask {
typealias Response = (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)
var mockResponse: Response
let completionHandler: ((NSData!, NSURLResponse!, NSError!) -> Void)?
init(response: Response, completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?) {
self.mockResponse = response
self.completionHandler = completionHandler
}
override func resume() {
completionHandler!(mockResponse.data, mockResponse.urlResponse, mockResponse.error)
}
}
}
Here's how I use it in a test:
Note that you still have to use an expectation even though there's no network delay.
func testRetrieveProductsValidResponse() {
let testBundle = NSBundle(forClass: self.dynamicType)
let filepath = testBundle.pathForResource("products", ofType: "txt")
let data = NSData(contentsOfFile: filepath!)
let urlResponse = NSHTTPURLResponse(URL: NSURL(string: "https://anyURL.com")!, statusCode: 200, HTTPVersion: nil, headerFields: nil)
MockSession.mockResponse = (data, urlResponse: urlResponse, error: nil)
let requestsClass = RequestManager()
requestsClass.Session = MockSession.self
let expectation = expectationWithDescription("ready")
requestsClass.retrieveProducts("N/R FOR TEST", branchID: "N/R FOR TEST", products: { (products) -> () in
XCTAssertTrue(products.count == 7)
expectation.fulfill()
}) { (error) -> () in
XCTAssertFalse(error == Errors.NetworkError, "Its a network error")
XCTAssertFalse(error == Errors.ParseError, "Its a parse error")
XCTFail("Error not covered by previous asserts. Shouln't get to here anyway.")
expectation.fulfill()
}
waitForExpectationsWithTimeout(3.0, handler: nil)
}
Finally, I have an accessible property on my RequestManager class that I can swap out with the MockSession when doing my tests.
var Session = NSURLSession.self
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
Sorry, new to iOS, etc. Just cannot convert it. Xcode beta 7.0 (7A121I)
Based on your included code, it looks like the syntax for your closure (completion handler) is incorrect. I'm not 100% sure if this will work as I don't have Xcode in front of me but try something like this:
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response, data, error) -> Void in
// code here
})
I am writing a FileProvider extension (NSFileProviderExtension) for iOS. When "itemChangedAtURL" is called, I want to take the data from the NSURL and commit it back to my server. The problem I am having is that I will use Alamofire to make a request call and the request will never fire. I have tried setting backgroundSessionConfigurationWithIdentifier and using a sharedContainerIdentifier but none of that worked either. The same alamofire calls worked outside of the FileProvider extension. Does anyone know how exactly I have to setup NSURLSession or Alamofire to get this to work correctly?
I ran into this similar problem with AFNetworking. I decided to wrap my calls in a block based on what I read in http://z43studio.com/2015/04/storage-providers/
override func itemChangedAtURL(url: NSURL) {
var manager = AFHTTPSessionManager()
var request = manager.requestSerializer.multipartFormRequestWithMethod("PUT", URLString: "http://somewhere.awesome", parameters: nil, constructingBodyWithBlock: { (data: AFMultipartFormData!) -> Void in
var res = data.appendPartWithFileURL(fileUrl, name: "custom_name", error: nil)
}, error: nil)
var semaphore = dispatch_semaphore_create(0)
let task:NSURLSessionDataTask = manager.uploadTaskWithStreamedRequest(request, progress: nil,
completionHandler: {(response: NSURLResponse!, responseObject: AnyObject!, error: NSError!) -> Void in
if ((error) != nil) {
println("ERROR")
// Attempt to recover - otherwise release
dispatch_semaphore_signal(semaphore)
} else {
println("SUCCESS")
dispatch_semaphore_signal(semaphore)
}
})
task.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
So I have the following function defined:
public typealias RESTClosure = (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void
public func queryAllFlightsWithClosure(completionHandler : RESTClosure) {
// code ....
}
I'm able to call this function as:
func myResponseHandler(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void {
// code ...
}
rest?.queryAllFlightsWithClosure(myResponseHandler)
From my understanding of Swift, however, if the final argument of a function is a closure it can be converted into a trailing closure ... but I've run into some syntax confusion:
Attempt #1
rest?.queryAllFlightsWithClosure() {
println("Called with Closure")
}
Error:
Tuples types '(response: NSURLResponse!, data NSData!, error: NSError!)' and '()' have different number of elements (3 vs. 0)
Attempt #2
rest?.queryAllFlightsWithClosure() (RESTClosure.self) { // Xcode told me to add a .self
//...code
}
Error: Missing argument for parameter #1 in call
I know I'm close ... but anybody can help me here?
Your parameters go inside the closure, and since there's just the one argument, you can even leave out the parentheses:
rest?.queryAllFlightsWithClosure {
(response: NSURLResponse!, data: NSData!, error: NSError!) in
// code ...
}
If you're going to be accessing self or any properties inside the closure, you'll want to include self in a capture list, like this:
rest?.queryAllFlightsWithClosure {
[weak self] (response: NSURLResponse!, data: NSData!, error: NSError!) in
// code ...
// note that self is optional inside this closure b/c of [weak self]
self?.doSomething()
}
Well this seems to compile
rest?.queryAllFlightsWithClosure() { RESTClosure in
// code ...
}