Testing a AFNetworking API with Kiwi - ios

I'm writing some tests for a networking API with kiwi.
Here is the relevant code:
//////////////////////////////////////////////////////
MyAPI.h
//////////////////////////////////////////////////////
#protocol MyAPIDelegate<NSObject>
-(void) onMyMethodCall:(id)response;
-(void) onFail:(NSError*)error message:(NSString*)message;
#end
#interface MyAPI : NSObject
#property (nonatomic, weak) id<MyAPIDelegate> delegate;
-(void) myMethodCall;
#end
//////////////////////////////////////////////////////
MyAPI.m
//////////////////////////////////////////////////////
#synthesize delegate
-(void) myMethodCall
{
NSDictionary *params = [Dictionary dictionaryWithObject:#"value1" forKey:#"param1"];
[self apiCallWithServerPath:#"logic/myMethodCall"
parameters:params
onSuccess:^(id response) {
[delegate onMyMethodCall];
}
onFailure:^(NSError *error, NSString* msg) {
[delegate onFail:error message:msg];
}];
}
-(void) apiCallWithServerPath:(NSString *)serverPath parameters:(NSDictionary *)parameters onSuccess:(void (^)(id))success onFailure:(void (^)(NSError *, NSString *))failure
{
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL urlWithString:#"http://www.myserver.com/"]];
[client setParameterEncoding:AFFormURLParameterEncoding];
[client postPath:serverPath
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
id response = [[JSONDecoder decoder] objectWithData:responseObject];
success(response);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"API call failed: error message = %#",[error localizedDescription]);
failure(error, [error localizedRecoverySuggestion]);
}];
}
//////////////////////////////////////////////////////
MyAPITest.m
//////////////////////////////////////////////////////
SPEC_BEGIN(MyAPITest)
describe(#"MyAPITest should", ^{
__block MyAPI *api;
__block MyAPI *delegateMock;
beforeEach(^{
delegateMock = [KWMock mockForProtocol:#protocol(MyAPIDelegate)];
api = [[MyAPI alloc] init];
api.delegate = delegateMock;
});
afterEach(^{
delegateMock = nil;
api = nil;
});
it(#"should go to the server and get an answer, dont care about the value atm", ^{
[[api should] receive:#selector(myMethodCall)];
KWCaptureSpy *spy = [delegateMock captureArgument:#selector(onMyMethodCall:) atIndex:0];
// I usually put a breakpoint here...
[api myMethodCall];
[[expectFutureValue(spy.argument) shouldEventually] beNonNil];
});
});
SPEC_END
This is a very simple test, that I'm building incrementally. Eventually I'd like to test the values inside spy.argument, but for now i'm only interested in making sure that it is not nil.
Running the test always fails with: [FAILED] Argument requested has yet to be captured.
Debugging doesn't work: When I try to put breakpoint in the test (where the comment says) its never steps into the method.
At the same time, if I put NSLog(s) inside MyAPI's myMethodCall, they are not printed on console.
Any help would be much appreciated.
/////////////////////// UPDATE //////////////////////////////////
Turns out that if I comment/remove this line (from the test):
[[api should] receive:#selector(myMethodCall)];
The test works. Any idea of why this line is causing the problem?

Related

difficulty understanding objective c code

i have been trying to implement the follow code , but I am having a hard time understanding the following code:
- (void)getRoutesWithStopName:(NSString *) stopName
success:(void (^)(NSArray *routes))success
error:(void (^)(NSString *errorMsg)) error
{
[[self AFManagerObject] POST:GET_ROUTES
parameters:#{#"params" : #{ #"stopName": [NSString stringWithFormat:#"%%%#%%",[stopName lowercaseString]]} }
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *routesRows = responseObject[#"rows"];
NSMutableArray *routes = [[NSMutableArray alloc] initWithCapacity:routesRows.count];
for(NSDictionary *dicRoute in routesRows)
{
FLBRoute *route = [[FLBRoute alloc] initWithAttrs:dicRoute];
[routes addObject:route];
}
success(routes);
}
failure:^(AFHTTPRequestOperation *operation, NSError *err) {
error(err.description);
}
];
}
I tried learning about blocks but I still can not understand what is going on here. Can you provide me a step by step explanation of the code ?
actually here used for webserviceCall
step-1
- (void)getRoutesWithStopName:(NSString *) stopName
success:(void (^)(NSArray *routes))success
error:(void (^)(NSString *errorMsg)) error
// here pass the one NSString and get the response using NSArray and failure using NSString
step-2
// here used AFNEtworking for call web service
//request block
[self AFManagerObject] -- NSObject class for AFNetworking method place.
POST:GET_ROUTES --> post is default function of request Type, GET_ROUTES --> your Macro class for Request URL
parameters --> send the parameter to server
[[self AFManagerObject] POST:GET_ROUTES
parameters:#{#"params" : #{ #"stopName": [NSString stringWithFormat:#"%%%#%%",[stopName lowercaseString]]} }
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
/*********** success response serlize and store into Array**********/
NSArray *routesRows = responseObject[#"rows"];
NSMutableArray *routes = [[NSMutableArray alloc] initWithCapacity:routesRows.count];
for(NSDictionary *dicRoute in routesRows)
{
FLBRoute *route = [[FLBRoute alloc] initWithAttrs:dicRoute];
[routes addObject:route];
// this is your NSObject class for save the details ,
}
success(routes);
/************** success stop **********/
}
/*********** error if request is fail ************/
failure:^(AFHTTPRequestOperation *operation, NSError *err) {
error(err.description);
}
];
/*********** error if request is stop ************/
I think you need to read a little more about callbacks https://en.m.wikipedia.org/wiki/Callback_(computer_programming) and blocks https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html and https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
Basically the method send a POST request and as you know it needs some time for the request to be sent to the server and for the server to respond. You don't want in this time your application to be freezed, so 2 callbacks are used, 1 for success case and 1 for failure case. A block callback is just a block of code that you want to be executed later, when the server will respond back, being a success or failure.

AFHTTPSessionManager - get unserialized/raw response body (NSData?)

I've subclassed AFHTTPSessionManager according to the recommended best practice for iOS 8 (in place of AFHTTPOperationManager, which I was using before).
I can grab the NSHTTPURLResponse from the task (except that has no body, only headers), and the callback returns the serialized responseObject which is fine.
Sometimes I need to log the response as a string or display it in a text field - there doesn't appear to be a way to do this natively using SessionManager? OperationManager allowed you to reference the raw response as an NSString:
operation.responseString;
I suppose I could stringify the serialized requestObject, but that seems like a lot of unnecessary overhead, and won't help if the response object is invalid JSON.
Here's my subclassed singleton:
#implementation MyAFHTTPSessionManager
+ (instancetype)sharedManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
And then to make a simple GET (which I've added to a block method), I can do:
[[MyAFHTTPSessionManager sharedManager] GET:_url parameters:queryParams success:^(NSURLSessionDataTask *task, id responseObject) {
completion(YES, task, responseObject, nil);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
completion(NO, task, nil, error);
}];
You can accomplish this by creating a custom response serializer that records the data and serializes the response using the standard response serializer, combining both the raw data and parsed object into a custom, compound response object.
#interface ResponseWithRawData : NSObject
#property (nonatomic, retain) NSData *data;
#property (nonatomic, retain) id object;
#end
#interface ResponseSerializerWithRawData : NSObject <AFURLResponseSerialization>
- (instancetype)initWithForwardingSerializer:(id<AFURLResponseSerialization>)forwardingSerializer;
#end
...
#implementation ResponseWithRawData
#end
#interface ResponseSerializerWithRawData ()
#property (nonatomic, retain) forwardingSerializer;
#end
#implementation ResponseSerializerWithRawData
- (instancetype)initWithForwardingSerializer:(id<AFURLResponseSerialization>)forwardingSerializer {
self = [super init];
if (self) {
self.forwardingSerializer = forwardingSerializer;
}
return self;
}
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error {
id object = [self.forwardingSerializer responseObjectForResponse:response data:data error:error];
// TODO: could just log the data here and then return object; so that none of the request handlers have to change
if (*error) {
// TODO: Create a new NSError object and add the data to the "userInfo"
// TODO: OR ignore the error and return the response object with the raw data only
return nil;
} else {
ResponseWithRawData *response = [[ResponseWithRawData alloc] init];
response.data = data;
response.object = object;
return response;
}
}
#end
Then set this serializer on your session manager:
#implementation MyAFHTTPSessionManager
+ (instancetype)sharedManager {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.responseSerializer = [[ResponseSerializerWithRawData alloc] initWithForwardingSerializer:instance.responseSerializer];
});
return instance;
}
Now in your completion handler you will get an instance of ResponseWithRawData:
[[MyAFHTTPSessionManager sharedManager] GET:_url parameters:queryParams success:^(NSURLSessionDataTask *task, id responseObject) {
ResponseWithRawData *responseWithRawData = responseObject;
NSLog(#"raw data: %#", responseWithRawData.data);
// If UTF8 NSLog(#"raw data: %#", [[NSString alloc] initWithData:responseWithRawData.data encoding:NSUTF8StringEncoding]);
// TODO: do something with parsed object
} failure:^(NSURLSessionDataTask *task, NSError *error) {
}];
I just whipped this up without compiling/testing, so I will leave it to you to debug and fill in the gaps.
You can access the “data” object directly from AFNetworking by using the “AFNetworkingOperationFailingURLResponseDataErrorKey” key so there is no need for subclassing the AFJSONResponseSerializer. You can the serialize the data into a readable dictionary. Here is some sample code to get JSON Data :
NSData *errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData: errorData options:kNilOptions error:nil];
Here is code to Get Status code in Failur block
NSHTTPURLResponse* r = (NSHTTPURLResponse*)task.response;
NSLog( #"success: %d", r.statusCode );

AFHTTPSessionManager get error response from server [duplicate]

I've been using AFNetworking 2.0 in my app.
I've noticed that if my web-service returns a 500 status code I do not get the body of the response.
Here is an example of my php code
try
{
$conn = new PDO( "sqlsrv:server=$serverName;Database = $database", $uid, $pwd);
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
return $conn;
}
catch( PDOException $e )
{
$response->status(500);
echo( "Connection Error: " . $e->getMessage() );
}
If I use a simple rest client this is an example of a response body.
Connection Error: SQLSTATE[08001]: [Microsoft][SQL Server Native Client 11.0]SQL Server Network Interfaces: Error Locating Server/Instance Specified [xFFFFFFFF].
However this seems to be the only response I can get from AFNetworking
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (JSON text did not start with array or object and option to allow fragments not set.) UserInfo=0x15e58fa0 {NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
This is the part of my objective-c code that does this.
...} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",error.description);
}];
Is there a way I can get the response body?
Edit: More code for clarification
Below is part of my subclass of AFHTTPSessionManager
#implementation MSMAMobileAPIClient
+ (MSMAMobileAPIClient *)sharedClient {
static MSMAMobileAPIClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[MSMAMobileAPIClient alloc] initWithDefaultURL];
});
return _sharedClient;
}
- (id)initWithDefaultURL {
return [self initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://%#/mamobile/index.php/" ,[[NSUserDefaults standardUserDefaults] stringForKey:#"serviceIPAddress"]]]];
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
self.responseSerializer = [AFCompoundResponseSerializer compoundSerializerWithResponseSerializers:#[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]]];
return self;
}
I tried setting the response serializer to a AFCompoundResponseSerializer but it didn't seem to make a difference
Below is an example of a subclass that I call the Librarian.
-(void)searchForItemWithString:(NSString *)searchString withCompletionBlock:(arrayBlock)block {
self.inventorySearchBlock = block;
NSDictionary *parameters = #{#"query": searchString};
[[MSMAMobileAPIClient sharedClient] GET:#"inventory/search" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
if (!responseObject) {
NSLog(#"Error parsing JSON");
} else {
//do stuff with the json dictionary that's returned..
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#",error.description);
}];
}
UPDATE: I have created a github repository to contain the latest code I am using. All changes will be posted there. https://github.com/Hackmodford/HMFJSONResponseSerializerWithData
The answer comes from this issue on github.
https://github.com/AFNetworking/AFNetworking/issues/1397
gfiumara is the dev who came up with this. I have only slightly modified his subclass of AFJSONResponseSerializer to include an actual string instead of the NSData
//MSJSONResponseSerializerWithData.h
#import "AFURLResponseSerialization.h"
/// NSError userInfo key that will contain response data
static NSString * const JSONResponseSerializerWithDataKey = #"JSONResponseSerializerWithDataKey";
#interface MSJSONResponseSerializerWithData : AFJSONResponseSerializer
#end
// MSJSONResponseSerializerWithData.m
#import "MSJSONResponseSerializerWithData.h"
#implementation MSJSONResponseSerializerWithData
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (*error != nil) {
NSMutableDictionary *userInfo = [(*error).userInfo mutableCopy];
userInfo[JSONResponseSerializerWithDataKey] = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSError *newError = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:userInfo];
(*error) = newError;
}
return (nil);
}
return ([super responseObjectForResponse:response data:data error:error]);
}
#end
Here is an example of how I use it in the failure block.
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"%#",[error.userInfo objectForKey:#"JSONResponseSerializerWithDataKey"]);
}];
You need to use AFCompoundSerializer to tell the AFNetworking framework how to process all of the possible responses it could receive. By default it will only try to map JSON. A compound serializer will work through the serializers until it finds one that doesn't raise an error.
You want to use:
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers
on AFCompoundResponseSerializer (in AFURLResponseSerialization.h).
You need to pass an array of serializers that can handle the response. One of the serializers in the array should be an instance of AFHTTPResponseSerializer to handle your error responses.
If you include my category in your project, it's as simple as the following:
[mySessionManager POST:#"some-api" parameters:params success:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
...
} failure:^(NSURLSessionDataTask *task, NSError *error) {
id responseObject = error.userInfo[kErrorResponseObjectKey];
... do something with the response ...
}];
Here's the code for my category. It swizzles AFURLSessionManager to inject a shim into the completion handler. The shim puts the response into the NSError's userInfo.
https://gist.github.com/chrishulbert/35ecbec4b37d36b0d608

AFHTTPSessionManager with multiple requests in rapid succession (AFNetworking 2.0)

i am new to iOS programming, still learning.
EDIT: !!!!!! Everything in my code works. My question is about the delegation pattern i use,
if i am generating problems in the background that i have no idea of, or if there is a better way to handle my situation in AFNetworking...
I have created an API for my app by subclassing AFHTTPSessionManager.
My API creates a singleton and returns it and supplies public functions for various requests. And those functions create parameter lists, and make GET requests on the server like this:
- (void)getCharacterListForKeyID:(NSString *)keyID vCode:(NSString *)vCode sender:(id)delegate
{
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[#"keyID"] = keyID;
parameters[#"vCode"] = vCode;
[self GET:#"account/Characters.xml.aspx" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
self.xmlWholeData = [NSMutableDictionary dictionary];
self.errorDictionary = [NSMutableDictionary dictionary];
NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
[XMLParser setShouldProcessNamespaces:YES];
XMLParser.delegate = self;
[XMLParser parse];
if ([delegate respondsToSelector:#selector(EVEAPIHTTPClient:didHTTPRequestWithResult:)]) {
[delegate EVEAPIHTTPClient:self didHTTPRequestWithResult:self.xmlWholeData];
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([delegate respondsToSelector:#selector(EVEAPIHTTPClient:didFailWithError:)]) {
[delegate EVEAPIHTTPClient:self didFailWithError:error];
}
}];
}
I was using a normal protocol/delegate method earlier. But once i make calls this API more than once like this: (IT WAS LIKE THIS:)
EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
client.delegate = self;
[client getCharacterListForKeyID:self.keyID vCode:self.vCode];
Previous call's delegate was being overwritten by next. So i changed to above style. Passing sender as an argument in the function:
EVEAPIHTTPClient *client = [EVEAPIHTTPClient sharedEVEAPIHTTPClient];
[client getCharacterListForKeyID:self.keyID vCode:self.vCode sender:self];
And i pass this sender to GET request's success and failure blocks.
What i wonder is : "Is this a good programming practice ?". Passing objects to blocks like this should be avoided if possible ? Is there any other more elegant way in AFHTTPSessionManager to handle this type of work (making same GET request over and over with different parameters and returning results to the respective request owners) more elegantly ?
Delegation pattern falters when it comes to simplicity and asynchronous request processing. You should be using blocks, here's an example
Your server class:
static NSString *const kNews = #"user_news/"; // somewhere above the #implementation
- (NSURLSessionDataTask *)newsWithPage:(NSNumber *)page
lastNewsID:(NSNumber *)lastNewsID
completion:(void (^)(NSString *errMsg, NSArray *news, NSNumber *nextPage))completionBlock {
return [self GET:kNews
parameters:#{#"page" : page,
#"news_id" : lastNewsID
}
success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *news = nil;
NSNumber *nextPage = nil;
NSString *errors = [self errors:responseObject[#"errors"]]; // process errors
if ([responseObject[#"status"] boolValue]) {
news = responseObject[#"news"];
nextPage = responseObject[#"next_page"];
[self assignToken];
}
completionBlock(errors, news, nextPage);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSString *errors = [self errors:error];
completionBlock(errors, nil, nil);
}];
}
The caller
- (void)dealloc {
[_task cancel]; // you don't want this task to execute if user suddenly removes your controller from the navigation controller's stack
}
- (void)requestNews {
typeof(self) __weak wself = self; // to avoid the retain cycle
self.task = [[GSGServer sharedInstance] newsWithPage:self.page
lastNewsID:self.lastNewsID
completion:^(NSString *errMsg, NSArray *news, NSNumber *nextPage) {
if (errMsg) {
[GSGAppDelegate alertQuick:errMsg]; // shortcut for posting UIAlertView, uses errMsg for message and "Error" as a title
return;
}
[wself.news addObjectsFromArray:news];
wself.lastNewsID = [wself.news firstObject][#"id"];
wself.page = nextPage;
[wself.tableView reloadData];
}];
}

how to unit testing AFNetworking request

i am making a GET request to retrieve JSON data with AFNetworking as this code below :
NSURL *url = [NSURL URLWithString:K_THINKERBELL_SERVER_URL];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
Account *ac = [[Account alloc]init];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:[NSString stringWithFormat:#"/user/%#/event/%#",ac.uid,eventID] parameters:nil];
AFHTTPRequestOperation *operation = [httpClient HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError *error = nil;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error];
if (error) {
}
[self.delegate NextMeetingFound:[[Meeting alloc]init] meetingData:JSON];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error){
}];
[httpClient enqueueHTTPRequestOperation:operation];
the thing is i want to create a unit test based on this data, but i dont want that the test will actually make the request. i want a predefined structure will return as the response. i am kind'a new to unit testing, and poked a little of OCMock but cant figure out how to manage this.
Several things to comment about your question.
First of all, your code is hard to test because it is creating the AFHTTPClient directly. I don't know if it's because it's just a sample, but you should inject it instead (see the sample below).
Second, you are creating the request, then the AFHTTPRequestOperation and then you enqueue it. This is fine but you can get the same using the AFHTTPClient method getPath:parameters:success:failure:.
I do not have experience with that suggested HTTP stubbing tool (Nocilla) but I see it is based on NSURLProtocol. I know some people use this approach but I prefer to create my own stubbed response objects and mock the http client like you see in the following code.
Retriever is the class we want to test where we inject the AFHTTPClient.
Note that I am passing directly the user and event id, since I want to keep things simple and easy to test. Then in other place you would pass the accout uid value to this method and so on...
The header file would look similar to this:
#import <Foundation/Foundation.h>
#class AFHTTPClient;
#protocol RetrieverDelegate;
#interface Retriever : NSObject
- (id)initWithHTTPClient:(AFHTTPClient *)httpClient;
#property (readonly, strong, nonatomic) AFHTTPClient *httpClient;
#property (weak, nonatomic) id<RetrieverDelegate> delegate;
- (void) retrieveEventWithUserId:(NSString *)userId eventId:(NSString *)eventId;
#end
#protocol RetrieverDelegate <NSObject>
- (void) retriever:(Retriever *)retriever didFindEvenData:(NSDictionary *)eventData;
#end
Implementation file:
#import "Retriever.h"
#import <AFNetworking/AFNetworking.h>
#implementation Retriever
- (id)initWithHTTPClient:(AFHTTPClient *)httpClient
{
NSParameterAssert(httpClient != nil);
self = [super init];
if (self)
{
_httpClient = httpClient;
}
return self;
}
- (void)retrieveEventWithUserId:(NSString *)userId eventId:(NSString *)eventId
{
NSString *path = [NSString stringWithFormat:#"/user/%#/event/%#", userId, eventId];
[_httpClient getPath:path
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary *eventData = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:NULL];
if (eventData != nil)
{
[self.delegate retriever:self didFindEventData:eventData];
}
}
failure:nil];
}
#end
And the test:
#import <XCTest/XCTest.h>
#import "Retriever.h"
// Collaborators
#import <AFNetworking/AFNetworking.h>
// Test support
#import <OCMock/OCMock.h>
#interface RetrieverTests : XCTestCase
#end
#implementation RetrieverTests
- (void)setUp
{
[super setUp];
// Put setup code here; it will be run once, before the first test case.
}
- (void)tearDown
{
// Put teardown code here; it will be run once, after the last test case.
[super tearDown];
}
- (void) test__retrieveEventWithUserIdEventId__when_the_request_and_the_JSON_parsing_succeed__it_calls_didFindEventData
{
// Creating the mocks and the retriever can be placed in the setUp method.
id mockHTTPClient = [OCMockObject mockForClass:[AFHTTPClient class]];
Retriever *retriever = [[Retriever alloc] initWithHTTPClient:mockHTTPClient];
id mockDelegate = [OCMockObject mockForProtocol:#protocol(RetrieverDelegate)];
retriever.delegate = mockDelegate;
[[mockHTTPClient expect] getPath:#"/user/testUserId/event/testEventId"
parameters:nil
success:[OCMArg checkWithBlock:^BOOL(void (^successBlock)(AFHTTPRequestOperation *, id))
{
// Here we capture the success block and execute it with a stubbed response.
NSString *jsonString = #"{\"some valid JSON\": \"some value\"}";
NSData *responseObject = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
[[mockDelegate expect] retriever:retriever didFindEventData:#{#"some valid JSON": #"some value"}];
successBlock(nil, responseObject);
[mockDelegate verify];
return YES;
}]
failure:OCMOCK_ANY];
// Method to test
[retriever retrieveEventWithUserId:#"testUserId" eventId:#"testEventId"];
[mockHTTPClient verify];
}
#end
The last thing to comment is that the AFNetworking 2.0 version is released so consider using it if it covers your requirements.

Resources