I am trying to learn unit testing with ocmock. I am finding it difficult to mock calls of another class from a class which i am unit testing.
Can some one suggest how to make mock call to KeyChainUtils class and HttpRequest class:
Code to unit test with OCMock:
#implementation UserProfileService {
+(BOOL) isValidUser
{
NSString* userId = [KeyChainUtil loadValueForKey:USER_ID]; //mock this call
bool isValidUser = NO;
if(userId && userId.length > 0){
NSDictionary* response = [HTTPDataService getJSONForURL:#"http://xtest.com/checkuserid" forRequestData:#{#"userid": userId}];
if(response && response[#"valid"]){
isValidUser = [response[#"valid"] boolValue];
}else{
NSLog(#"error in connecting to server. response => %#", response);
}
}
return isValidUser;
}
}
Starting with OCMock release 2.1, we can stub the class method. Please refer this link for more information: http://www.ocmock.org/features/
So, we can stub the class method like this:
id keyChainUtilMock = [OCMockObject mockForClass:[KeyChainUtil class]];
[[[keyChainUtilMock stub] andReturn:#"aasdf"] loadValueForKey:USER_ID];
NSString* userId = [KeyChainUtil loadValueForKey:USER_ID];
NSLog(#" stubbed value-->%#", userId);
So, after running this particular piece of code. The actual class method is not called here and the stubbed value is returned. I hope this helps you.
Related
I need to make the SDK for one of my project, What I need to do is make a search class which return the response from the web API,
I'm looking for something like this.
SearchController * sc = [[SearchController alloc] initWithSearchText:#"google"];
This method is called every time, when I change the textField text, and the response I get should be in block.
Any help and reference would be appreciated.
SearchController.h
typedef void (^SearchHandler)(id results,NSError *error);
#interface SearchController : NSObject
{
SearchHandler searchBlock;
}
-(void)searchWithText:(NSString *)strText Results:(SearchHandler)searchResult;
SearchController.m
-(void)searchWithText:(NSString *)strText Results:(SearchHandler)searchResult
{
searchBlock = [searchResult copy];
//call api for text search
}
-(void)getResponseOfApi
{
//code where get api response
searchBlock(apiResonse,apiError);
}
call SearchController api with block
SearchController * sc = [[SearchController alloc] init];
[sc searchWithText:#"google" Results:^(id results, NSError *error) {
//block with api response
}];
You can make one block method in your SearchController and get response of type you want.
-(void)executeSearchRequestWithHandler:(void (^)(BOOL result))completionHandler {
//Do your api calling and than handle completionHandler
completionHandler(YES);
//You can set completionHandler type that you want like NSArray,NSString,NSDictionary etc
}
Now call this method like this.
[sc executeSearchRequestWithHandler:^(BOOL result) {
if (result) {
}
}];
Note: Here I have created block with BOOL parameter, you can set parameter type you want from response like NSArray,NSDictionary,NSString etc.
I've working on/learning this all afternoon.
Following an example here: http://themainthread.com/blog/2012/09/communicating-with-blocks-in-objective-c.html, I have managed to setup a callback to get the result of an asynchronous call to a web service i have.
The web service takes a key code and the app transforms it and passes it back for authentication.
With my code below, how can I change the method from a void to an NSString that I can call to return my pass code?
-(void) showPassCode{
getAuthCodeAndMakePassCodeCompleteBlock callback = ^(BOOL wasSuccessful, NSString *passCode) {
if (wasSuccessful) {
NSLog(#"Code is: %#", passCode);
} else {
NSLog(#"Unable to fetch code. Try again.");
}
};
[self getAuthCodeAndMakePassCode:#"myAuthCode" withCallback:callback];
}
Ideally, I want it to work or look like this:
-(NSString *) strPassCode{
getAuthCodeAndMakePassCodeCompleteBlock callback = ^(BOOL wasSuccessful, NSString *passCode) {
if (wasSuccessful) {
return passCode;
} else {
return nil;
}
};
[self getAuthCodeAndMakePassCode:#"myAuthCode" withCallback:callback];
}
Without knowing the specifics of your code and how you query the server, I have to imagine it would look something like:
-(void)getAuthCodeWithCallback:(void (^)(NSString* authCode))callback
{
//make server call, synchronously in this example
NSString* codeReturnedFromServer = [self getServerCodeSynchronous];
callback(codeReturnedFromServer);
}
//some calling code
[self getAuthCodeWithCallback:^(NSString* authCode) {
NSLog(#"Code is: %#", authCode);
}];
If the method that gets your auth code from the server is asynchronous, it would look something like this:
-(void)getAuthCodeWithCallback:(void (^)(NSString* authCode))callback
{
//make server call, asynchronously in this example
[self someMethodCallToQueryCodeFromServerWithCallback:^(NSError* error, NSString* code) {
if (error) {
//handle error
}
else
callback(code);
}
}
I have been trying to use dispatch_async in a method that returns a result. However, I observed that the method returns before executing the dispatch_async block. Due to this I'm not getting the results I expect. Here is some code that demonstrates my problem.
-(BOOL) isContactExists {
BOOL isContactExistsInXYZ = YES;
UserId *userId = contact.userId;
dispatch_async(dispatch_get_main_queue(), ^
{
iOSContact *contact = [iOSContact contactForUserId:userId];
if (nil == contact)
{
isContactExistsInXYZ = NO;
}
});
return isContactExistsInXYZ;
}
This method isContactExists is called somewhere else and based on the response from that method I have to do some stuff. But every time, the value of isContactExistsInXYZ is not what I expect. How do I handle dispatch_async in this situation?
If your going the block route your method needs to look something like this.
- (void)isContactExistsWithCompletionHandler:(void(^)(BOOL exists)) completion
{
dispatch_async(dispatch_get_main_queue(), ^
{
BOOL isContactExistsInXYZ = YES;
UserId *userId = contact.userId;
iOSContact *contact = [iOSContact contactForUserId:userId];
if (nil == contact)
{
isContactExistsInXYZ = NO;
}
completion(isContactExistsInXYZ);
});
}
And where you are calling it something like this.
[someObject isContactExistsWithCompletionHandler:^(BOOL exists) {
// do something with your BOOL
}];
You should also consider placing your heavy operations in a other que than main. Like this.
- (void)isContactExistsWithCompletionHandler:(void(^)(BOOL exists)) completion
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
dispatch_async(queue, ^
{
BOOL isContactExistsInXYZ = YES;
UserId *userId = contact.userId;
iOSContact *contact = [iOSContact contactForUserId:userId];
if (nil == contact)
{
isContactExistsInXYZ = NO;
}
dispatch_async(dispatch_get_main_queue(), ^
{
completion(isContactExistsInXYZ);
});
});
}
You need to respect that what you are trying to do is asynchronous and embrace that. This means not using a return value. Instead you can write your method to take a callback block as a parameter. Then, when your asynchronous check is complete you can call the block with the result.
So your method signature would become:
- (void)checkIfContactExistsWithCompletion:(ContactExistsBlock)completion {
Where ContactExistsBlock is a block definition with no return and probably a single BOOL parameter.
typedef void (^ContactExistsBlock) (BOOL exists);
The reason is dispatch_async(dispatch_get_main_queue(), ^does not wait until execution is done. You are probably messing up stuff there. Normally, this is used to update UI asynchronously along with other server content getting downloaded in some other thread. Try using dispatch_sync instead.
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.
Ho do I stub a method used in the init method?
The related methods in my class:
- (id)init
{
self = [super init];
if (self) {
if (self.adConfigurationType == AdConfigurationTypeDouble) {
[self configureForDoubleConfiguration];
}
else {
[self configureForSingleConfiguration];
}
}
return self;
}
- (AdConfigurationType)adConfigurationType
{
if (adConfigurationType == NSNotFound) {
if ((random()%2)==1) {
adConfigurationType = AdConfigurationTypeSingle;
}
else {
adConfigurationType = AdConfigurationTypeDouble;
}
}
return adConfigurationType;
}
My test:
- (void)testDoubleConfigurationLayout
{
id mockController = [OCMockObject mockForClass:[AdViewController class]];
AdConfigurationType type = AdConfigurationTypeDouble;
[[[mockController stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];
id controller = [mockController init];
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNil([controller largeAd], #"Expected nil here");
}
My result:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[AdViewController]: unexpected method invoked: smallAdRight '
So how will I access the AdViewController in the OCMockObject?
If you use the mockForClass: method you will need to provided stubbed implementations for each and every method that are called in the mocked class. including your call into it with [controller smallAdRight] in your first test.
Instead you can use the niceMockForClass: method which will just ignore any messages which are not mocked.
Another alternative is to instantiate your AdViewController and then create a partial mock for it using the partialMockForObject: method. This way the internals of the controller class will do the main part of the work.
Just a though... are you trying to test the AdViewController or a class which uses it? It appears that you are trying to mock the entire class and then test if it still behaves normally. If you want to test that AdViewController behaves as expected when certain values are injected then your best option is most likely the partialMockForObject: method:
- (void)testDoubleConfigurationLayout {
AdViewController *controller = [AdViewController alloc];
id mock = [OCMockObject partialMockForObject:controller];
AdConfigurationType type = AdConfigurationTypeDouble;
[[[mock stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];
// You'll want to call init after the object have been stubbed
[controller init]
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNil([controller largeAd], #"Expected nil here");
}