I'm trying to test the URL/path against a request is (or would be) made from a REST-client class using OCMock. The client uses RestKit (which uses AFNetworking) for the communication.
Therefore my plan was to:
Create a block which checks if a AFHTTPRequestOperation URL is the desired one.
Create a partial mock of the of AFHTTPClient.
Mock (stub) the enqueueHTTPRequestOperation: method with the block (of 1.).
Create a mock of RKObjectManager and set its HTTPClient property to the
AFHTTPClient partial-mock (of 2.).
Init an instance of my client-class with the mock-manager (from 4.).
Invoke the method of this instance of which the URL is to be checked.
Verify that enqueueHTTPRequestOperation: was invoked.
I'm not sure if I'm getting OCMock wrong because I couldn't find examples on mocking methods that take one or more arguments like I need to. ...never the less, this is how I tried to achieve the goal:
void (^checkBlock)(NSInvocation *) = ^(NSInvocation *invocation) {
AFHTTPRequestOperation *requestOperation = nil;
[invocation getArgument:&requestOperation atIndex:0];
XCTAssert(requestOperation != nil);
NSString *path = requestOperation.request.URL.path;
XCTAssert([path containsString:#"/rest/user/authenticate"]);
};
AFHTTPClient *httpClientMock = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:FAServerUrl]];
OCMPartialMock(httpClientMock);
[OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg isNotNil]]) andDo:checkBlock];
RKObjectManager *objectManager = OCMClassMock([RKObjectManager class]);
[OCMStub([objectManager HTTPClient]) andReturn:httpClientMock];
FAUserClient *userClient = [[FAUserClient alloc] initWithUser:nil objectManager:objectManager];
[userClient getAccessTokenForUsername:#"testuser"
password:#"pass123"
success:^(NSString *token, NSArray *roles) {
}
failure:^(NSInteger errorCode, NSError *error) {
}];
OCMVerify([httpClientMock enqueueHTTPRequestOperation:OCMOCK_ANY]);
But on [OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg isNotNil]]) andDo:checkBlock]; I get an EXC_BAD_ACCESS (code=1).
Apparently creating the mock-stub (with OCMStub) invokes the to be stubbed method, with the given [OCMArg isNotNil]. I thought A: the parameter just has a declarative meaning and B: this creates a stub and does not invoke the method right away.
Any help or suggestions leading into the "right" direction would be greatly appreciated.
EDIT:
As well tried:
OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg checkWithBlock:^BOOL(id obj) {
AFHTTPRequestOperation *request = (AFHTTPRequestOperation *)obj;
NSString *path = request.request.URL.path;
XCTAssert([path containsString:#"/rest/user/authenticate"]);
return YES;
}]]);
...with the same "result".
Best,
gabriel
Edit
Looked more closely. You are calling OCMPartialMock(httpClientMock). This does not convert the object you call it on, it returns a partial mock. Capture the result in a variable.
AFHTTPClient *httpClientMock = OCPartialMock([[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:FAServerUrl]]);
You should still make the change noted below in your andDo: block. You can also use the "modern" syntax for this:
OCMStub([myObject myMethod]).andDo(myBlock);
Original
I think the issue might be the code in the andDo: block.
[invocation getArgument:&requestOperation atIndex:0];
For all Objective-C methods, NSInvocation has two default arguments, self and _cmd at indexes 0 and 1. Try getting the argument at index 2.
You might also consider including NSInvocation+OCMAdditions which gives you getArgumentAtIndexAsObject . Another alternative is using [OCMArg checkWithBlock:] in which the arg is handed to your evaluation block directly.
Related
I am trying to use OCMock library. I am trying to create mock of class object, but it is failing to verify the method. I am unable to understand why the tests are failing.
#interface MyClass:NSObject
+(void) someMethod;
#end
#implementation MyClass
+(void) someMethod
{
NSError* error = nil;
if (![Utility isValidPropWithError:&error])
{
[Logger log:LoggerLevelWarning message:[error localizedDescription] className:className];
}
}
#end
Test :
-(void)testIfLoggerIsset{
id partialMockLogger = OCMClassMock([Logger class]);
id partialMockUtility = OCMClassMock([Utility class]);
id partialMockClass = OCMClassMock([MyClass class]);
NSError *error = nil;
OCMExpect([partialMockUtility isValidPropWithError:&error]);
[MyClass someMethod];
//This works fine.
OCMVerifyAll(partialMockClass);
NSString *className = #"classname";
//This is failing...
OCMVerify([partialMockUtility isValidPropWithError:&error]);
OCMVerifyAll(partialMockUtility);
//This is failing...
OCMVerify([partialMockLogger log:LoggerLevelWarning message:[error localizedDescription] className:className]);
[partialMockUtility stopMocking];
[partialMockLogger stopMocking];
}
In the above code, although [Utility isValidPropWithError:&error]; is called OCMVerify([partialMockUtility isValidPropWithError:&error]);is failing.
Several things here:
First, OCMVerify([partialMockUtility isValidPropWithError:&error] is failing because you are expecting the address of the NSError object you created in the test to be passed to isValidPropWithError:, but in MyClass +someMethod you are creating a different NSError object. The addresses of two different objects will not be the same.
To fix this, change your expectation and verification to:
OCMExpect([partialMockUtility isValidPropWithError:(NSError __autoreleasing**)[OCMArg anyPointer]]);
OCMVerify([partialMockUtility isValidPropWithError:(NSError __autoreleasing**)[OCMArg
and just ignore the actual value of the parameter and expect that it's going to be an NSError pointer (since you're creating it inside of someMethod, there's no way to know what it's going to be before you call the method).
Second, since you are already explicitly verifying +isValidPropWithError, OCMVerifyAll(partialMockUtility) isn't going to verify anything. You should either explicitly verify all of your expectations, or simply use OCMVerifyAll(partialMockUtility) and let it verify all your expectations and don't bother with expecting the specific call. OCMVerifyAll will verify everything you expect on the mock object you give it. This isn't going to cause a test failure - both calls will pass, since you've already verified the call the first time, the call to OCMVerifyAll() isn't going to have anything to verify, so it will pass.
Last, OCMVerify([partialMockLogger log:LoggerLevelWarning message:[error localizedDescription] className:className]); is failing because you didn't set an expectation for it.
I have been asking and trying to understand how completion handlers work. Ive used quite a few and I've read many tutorials. i will post the one I use here, but I want to be able to create my own without using someone else's code as a reference.
I understand this completion handler where this caller method:
-(void)viewDidLoad{
[newSimpleCounter countToTenThousandAndReturnCompletionBLock:^(BOOL completed){
if(completed){
NSLog(#"Ten Thousands Counts Finished");
}
}];
}
and then in the called method:
-(void)countToTenThousandAndReturnCompletionBLock:(void (^)(BOOL))completed{
int x = 1;
while (x < 10001) {
NSLog(#"%i", x);
x++;
}
completed(YES);
}
Then I sorta came up with this one based on many SO posts:
- (void)viewDidLoad{
[self.spinner startAnimating];
[SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
self.usersArray = users;
[self.tableView reloadData];
}];
}
which will reload the tableview with the received data users after calling this method:
typedef void (^Handler)(NSArray *users);
+(void)fetchUsersWithCompletionHandler:(Handler)handler {
NSURL *url = [NSURL URLWithString:#"http://www.somewebservice.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
[request setHTTPMethod: #"GET"];
**// We dispatch a queue to the background to execute the synchronous NSURLRequest**
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Perform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) { **// If an error returns, log it, otherwise log the response**
// Deal with your error
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"HTTP Error: %d %#", httpResponse.statusCode, error);
return;
}
NSLog(#"Error %#", error);
return;
}
**// So this line won't get processed until the response from the server is returned?**
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
NSArray *usersArray = [[NSArray alloc] init];
usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
// Finally when a response is received and this line is reached, handler refers to the block passed into this called method...so it dispatches back to the main queue and returns the usersArray
if (handler){
dispatch_sync(dispatch_get_main_queue(), ^{
handler(usersArray);
});
}
});
}
I can see it in the counter example, that the called method (with the passed block) will never exit the loop until it is done. Thus the 'completion' part actually depends on the code inside the called method, not the block passed into it?
In this case the 'completion' part depends on the fact that the call to NSURLRequest is synchronous. What if it was asynchronous? How would I be able to hold off calling the block until my data was populated by the NSURLResponse?
Your first example is correct and complete and the best way to understand completion blocks. There is no further magic to them. They do not automatically get executed ever. They are executed when some piece of code calls them.
As you note, in the latter example, it is easy to call the completion block at the right time because everything is synchronous. If it were asynchronous, then you need to store the block in an instance variable, and call it when the asynchronous operation completed. It is up to you to arrange to be informed when the operation completes (possibly using its completion handler).
Do be careful when you store a block in an ivar. One of your examples includes:
self.usersArray = users;
The call to self will cause the block to retain self (the calling object). This can easily create a retain loop. Typically, you need to take a weak reference to self like this:
- (void)viewDidLoad{
[self.spinner startAnimating];
__weak typeof(self) weakSelf = self;
[SantiappsHelper fetchUsersWithCompletionHandler:^(NSArray *users) {
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf setUsersArray:users];
[[strongSelf tableView] reloadData];
}
}];
}
This is a fairly pedantic version of the weakSelf/strongSelf pattern, and it could be done a little simpler in this case, but it demonstrates all the pieces you might need. You take a weak reference to self so that you don't create a retain loop. Then, in the completely block, you take a strong reference so that self so that it can't vanish on you in the middle of your block. Then you make sure that self actually still exists, and only then proceed. (Since messaging nil is legal, you could have skipped the strongSelf step in this particular case, and it would be the same.)
Your first example (countToTenThousandAndReturnCompletionBLock) is actually a synchronous method. A completion handler doesn't make much sense here: Alternatively, you could call that block immediately after the hypothetical method countToTenThousand (which is basically the same, just without the completion handler).
Your second example fetchUsersWithCompletionHandler: is an asynchronous method. However, it's actually quite suboptimal:
It should somehow signal the call-site that the request may have failed. That is, either provide an additional parameter to the completion handler, e.g. " NSError* error or us a single parameter id result. In the first case, either error or array is not nil, and in the second case, the single parameter result can be either an error object (is kind of NSError) or the actual result (is kind of NSArray).
In case your request fails, you miss to signal the error to the call-site.
There are code smells:
As a matter of fact, the underlying network code implemented by the system is asynchronous. However, the utilized convenient class method sendSynchronousRequest: is synchronous. That means, as an implementation detail of sendSynchronousRequest:, the calling thread is blocked until after the result of the network response is available. And this_blocking_ occupies a whole thread just for waiting. Creating a thread is quite costly, and just for this purpose is a waste. This is the first code smell. Yes, just using the convenient class method sendSynchronousRequest: is by itself bad programming praxis!
Then in your code, you make this synchronous request again asynchronous through dispatching it to a queue.
So, you are better off using an asynchronous method (e.g. sendAsynchronous...) for the network request, which presumable signals the completion via a completion handler. This completion handler then may invoke your completion handler parameter, taking care of whether you got an actual result or an error.
H guys,
I have been trying for ages to find some good examples on how to use Kiwi testing to test delegate methods, asynchronously.
I have a manager class that defines the protocols for testing, with a pass and fail method returned in the delegate. Can anyone provide sample code on how to do this? Can I make the test class itself implement the to call the methods on the manager?
Thanks guys
You can do like in example
SPEC_BEGIN(IFStackOverflowRequestSpec)
describe(#"IFStackOverflowRequestSpec", ^
{
context(#"question request", ^
{
__block IFViewController *controller = nil;
beforeEach(^
{
controller = [IFViewController mock];
});
it(#"should conform StackOverflowRequestDelegate protocol", ^
{
[[controller should] conformToProtocol:#protocol(StackOverflowRequestDelegate)];
});
it(#"should recieve receivedJSON", ^
{
NSString *questionsUrlString = #"http://api.stackoverflow.com/1.1/search?tagged=iphone&pagesize=20";
IFStackOverflowRequest *request = [[IFStackOverflowRequest alloc] initWithDelegate:controller urlString:questionsUrlString];
[[request fetchQestions] start];
[[[controller shouldEventuallyBeforeTimingOutAfter(3)] receive] receivedJSON:any()];
});
it(#"should recieve fetchFailedWithError", ^
{
NSString *fakeUrl = #"asda";
IFStackOverflowRequest *request = [[IFStackOverflowRequest alloc] initWithDelegate:controller urlString:fakeUrl];
[[request fetchQestions] start];
[[[controller shouldEventuallyBeforeTimingOutAfter(1)] receive] fetchFailedWithError:any()];
});
});
});
Full example can be founded on this link.
You can do what I think you're trying to achieve by creating a mock object that stands in for the delegate, and then checking that the mock object receives the delegate callbacks that you expect. So the process would look like:
create a mock object that conforms to the delegate protocol:
id delegateMock = [KWMock mockForProtocol:#protocol(YourProtocol)];
set the mock as the delegate of your manager class:
[manager setDelegate:delegateMock];
create an object containing the data that will be returned by your manager class:
NSString *response = #"foo";
set the assertion that the mock should eventually be called with the method and response object (in this case, I'm expecting to receive managerRepliedWithResponse and foo)
[[[delegateMock shouldEventually] receive] managerRepliedWithResponse:response];
call the method under test:
[manager performMyMethod];
The key is setting the expectation before you call the method, and using shouldEventually which delays the assertion being checked and gives the manager object time to perform the method.
There's a range of expectations you can also use that are listed on the Kiwi wiki - https://github.com/allending/Kiwi/wiki/Expectations
I've written the process up in more detail in a post on my site, albeit that it's more specifically geared-up to the situation I was dealing with.
Using iOS 5's new TWRequest API, I've ran into a brick wall related with block usage.
What I need to do is upon receiving a successful response to a first request, immediately fire another one. On the completion block of the second request, I then notify success or failure of the multi-step operation.
Here's roughly what I'm doing:
- (void)doRequests
{
TWRequest* firstRequest = [self createFirstRequest];
[firstRequest performRequestWithHandler:^(NSData* responseData,
NSHTTPURLResponse* response,
NSError* error) {
// Error handling hidden for the sake of brevity...
TWRequest* secondRequest = [self createSecondRequest];
[secondRequest performRequestWithHandler:^(NSData* a,
NSHTTPURLResponse* b,
NSError* c) {
// Notify of success or failure - never reaches this far
}];
}];
}
I am not retaining either of the requests or keeping a reference to them anywhere; it's just fire-and-forget.
However, when I run the app, it crashes with EXC_BAD_ACCESS on:
[secondRequest performRequestWithHandler:...];
It executes the first request just fine, but when I try to launch a second one with a handler, it crashes. What's wrong with that code?
The methods to create the requests are as simple as:
- (TWRequest*)createFirstRequest
{
NSString* target = #"https://api.twitter.com/1/statuses/home_timeline.json";
NSURL* url = [NSURL URLWithString:target];
TWRequest* request = [[TWRequest alloc]
initWithURL:url parameters:params
requestMethod:TWRequestMethodGET];
// _twitterAccount is the backing ivar for property 'twitterAccount',
// a strong & nonatomic property of type ACAccount*
request.account = _twitterAccount;
return request;
}
Make sure you're keeping a reference/retaining the ACAccountStore that owns the ACAccount you are using to sign the TWRequests.
If you don't, the ACAccount will become invalid and then you'll get EXC_BAD_ACCESS when trying to fire a TWRequest signed with it.
I'm not familiar with TW*, so consider this a wild guess ... try sending a heap-allocated block:
[firstRequest performRequestWithHandler:[^ (NSData *responseData, ...) {
...
} copy]];
To clarify, I think the block you're sending is heap-allocated, so while TW* might be retaining it, it won't make any difference if it has already gone out of scope.
I am testing out RestKit and need to access different BaseUrls and also sometimes access a web service with the same baseUrl from different places "at once", lastly I also need to access the same baseUrl with different ressourcePaths in the same controller.
In my app delegate I set up the RKObjectManager singleton like this.
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:kBaseUrl];
[objectManager registerClass:[EntityClass1 class] forElementNamed:#"element1"];
[objectManager registerClass:[EntityClass2 class] forElementNamed:#"element2"];
.
.
.
etc.
The singleton approach is really easy to work with, I however can't figure out how to separate the different web service calls.
In MyViewController, which implement the RKObjectLoaderDelegate, I will have the two methods:
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
//stuff with result
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
//stuff with error
}
This causes no problems when MyViewController uses one RKObjectManager singleton to access one ressourcePath with one baseUrl.
If I start different requests in this way:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:FLICKRPath delegate:self]
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:FOURSQUAREPath delegate:self]
and so on, within the same MyController, my problem is that FLICKRPath and FOURSQUAREPath of course has different baseUrl, but the RKObjectManager only has one?
If I get this working and can have different RKObjectManagers another problem arises.
The delegate methods didLoadObjects and didFailWithError will receive results from both RKObjectManagers and I can't see any other way to tell them apart than from their baseUrls. Potentially comparing each return value with a baseUrl and, even worse, a ressourcePath, in the delegate method does not appeal to me at all.
If I have different RKObjectManagers I guess I could pass them different delegates and build classes dedicated to deal with the return values from different baseUrls and ressourcePaths. This would mean I had to build yet another abstraction on top of MyController and RestKit, which also seems messy.
I have a strong feeling I am going about this in the wrong way, the RestKit source is very impressive which indicates that is me fighting the framework. I would really appreciate some best practice insights on the subject. I have been through all the resources and examples that I could find but have not seen the above use case. It is always one RKObjectManager, one baseUrl and one ressourcePath.
Thank you in advance.
Since there is no accepted answer yet: using multiple object managers is quite simple using RestKit.
From the Wiki (Using Multiple Base URLs (and Multiple Object Managers):
The first object manager you create will be the shared singleton
RestKit uses by default. But by creating additional object managers,
you can pull from their BaseURLs as needed, just be sure to retain
these new managers.
RKObjectManager *flickrManager =
[RKObjectManager objectManagerWithBaseURL:flickrBaseUrl]; // <-- shared singleton
RKObjectManager *foursquareManager =
[[RKObjectManager objectManagerWithBaseURL:foursquareBaseUrl] retain]; // <-- you must retain every other instance.
Depending on your application, you may want to put this second object
manager in a more accessible place, like a retained property on the
AppDelegate, so that it's easy to pull from as needed.
In the event that you need to differentiate between the results from
your two (or more) object managers, simply set an identifier in the
userData for the queries.
- (void)someAction(id)sender {
// .......
RKObjectLoader* loader = [[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/whatever" delegate:self];
loader.userData = #"foursquare";
// or do this, if you need a number instead of a string
loader.userData = [NSNumber numberWithInt:1234];
// .......
}
//Then when the delegate comes back you can cast it into a string or number as appropriate:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
// .......
NSString* source = (NSString*) objectLoader.userData;
// or, if you did the NSNumber instead:
NSNumber* source = (NSNumber*) objectLoader.userData;
// .......
}
API change:
RKObjectLoader* loader = [[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/whatever" delegate:self];
doesn't compile in RestKit v.0.10.3 (loadObjectsAtResourcePath:delegate: returns void). That method just wraps a few lines of code, though, so you can still get at the loader, and add userData, with the following:
RKObjectLoader *loader = [[RKObjectManager sharedManager] loaderWithResourcePath:resourcePath];
loader.userData = #"SOMEDATA";
loader.delegate = self;
loader.method = RKRequestMethodGET;
[loader send];
(adding note in case other new users run into the same issues I did).
And by the way, since userData property is also available on RKRequest, you can use the same approach for loading/identifying requests.
For example, some post request:
RKClient * client = [RKClient sharedClient];
[client post:#"/your-api-path" usingBlock:^(RKRequest *request) {
request.userData = #"<some-object-you-can-check-in-delegate-callback>";
request.params = someParamsForRequest;
request.delegate = <delegate you want to call when request is finished>;
}];
How about using objectLoader.
You'll find the mapped object type/Class objectLoader.objectMapping.objectClass and add your conditions based on it instead of the url
-(void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
// your condition based on -> objectLoader.objectMapping.objectClass
}
Hope it will help
Possible approach is to introduce one singletone for each base url.
You can instantiate as many RKObjectManager objects as you want. However, only the first one will become shared. Look into initWithHTTPClient: sources.
if (nil == sharedManager) {
[RKObjectManager setSharedManager:self];
}
We can't use default sharedManager method to target specific object manager but we can easily implement our own singleton. Here's an example for Google Maps object manager:
#implementation GMObjectManager
+ (GMObjectManager*)sharedManager
{
static GMObjectManager *manager; // keep reference
if (!manager) {
// init with custom base url
NSURL *baseUrl = [NSURL URLWithString:kGMBaseUrl];
manager = [GMObjectManager managerWithBaseURL:baseUrl];
}
return manager;
}
- (id)initWithHTTPClient:(AFHTTPClient *)client
{
self = [super initWithHTTPClient:client];
if (self) {
// additional initialization
}
return self;
}
#end
Usage:
CGObjectManager *googleMapsManager = [GMObjectManager sharedInstance];