Can I build a Unit Test for an External Service (iPhone)? - iphone

My app registers with an external service. The service phones me up with a two-digit code that I have to enter in (first time only) in order to use the service. The rest of the calls to the service work fine afterwards.
How would I set up a unit test for a method that isn't complete until a response code is entered out of band?
Any ideas?
iOS SDK 4.2

Use OCMock to mock the service, that allows you to use stubbing and return the expected result.
For example:
Let's say this is your service:
#interface ServiceClass
- (NSString *) fetchData;
#end
This would be your mock service
id serviceMock = [[OCMockObject niceMockForClass:[ServiceClass class]];
[[[serviceMock stub] andreturn:#"56"] fetchData];
// we tell the mock to return the string "56" when the method fetchData is called
SomeViewController *mvc = [[SomeViewController alloc] init];
mvc.webService = serviceMock;
// Here we are injecting a mock into a view controller
You can use some Dependency injection techniques to inject this mock into your view controller
#synthesize webService = _webService;
- (IBAction)buttonClicked:(id)sender
{
NSString *result = [self.webService fetchData];
}
- (ServiceClass *)webService
{
if (!_webService)
{
_webService = [[ServiceClass alloc] init];
}
return _webService;
}

Related

How to access the method in the Private API framework and pass the value to it?

First - I know private frameworks/APIs won't get me to the AppStore, this is for private use/research only.
So as for the research purpose I chose MFMessageComposer I want to disable the editing of any inputs both of which are being passed from the code.
I tried to put my hands on this and I coded in the following manner . What I did is I took the path of the private framework and accessed a particular class called as CKSMSComposeController which have the above mentioned methods . I referred the class dump classes of the ChatKit.framework https://github.com/nst/iOS-Runtime-Headers/blob/master/PrivateFrameworks/ChatKit.framework/CKSMSComposeController.h
I am getting the logs of NSLog(#"Result %#", success ? #"YES" : #"NO"); as YES but still I am unable to disable the edit of recepients even after passing NO to the selector above
Can someone tell am I passing the parameter in a correct way ? Because -(void)setCanEditRecipients:(BOOL)arg1;` which is a method in the private framework accepts bool as parameter and I am passing NO in above code
This is just for internal research on private frameworks. Where I am doing wrong ?.Please tell
Class methods start with + and instance methods start with - in Objective-C.
// Following is an instance method because it starts with `-`
- (void)setCanEditRecipients:(bool)arg1;
Above method will NOT work with following code.
Class CKSMSComposeController = NSClassFromString(#"CKSMSComposeController");
SEL sel = NSSelectorFromString(#"setCanEditRecipients:");
// `CKSMSComposeController` is a class - NOT an instance
if ([CKSMSComposeController respondsToSelector:sel]) {
// will not enter if's body
}
On top of all this - you shouldn't create an instance of your own and do customizations on that. You should do the customizations on the instance that's presented by the system on screen.
Here's how you can try that -
- (void) showMessageComposeViewController {
if ([MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController* messageController = [[MFMessageComposeViewController alloc] init];
messageController.recipients = #[#"555-555-5555"];
messageController.body = #"Example message";
[self presentViewController:messageController animated:YES completion:^{
// Allow enough time for the UI to be loaded fully
dispatch_after(1, dispatch_get_main_queue(), ^{
// Since `MFMessageComposeViewController` is a `UINavigationController`, we can access it's first view controller like this
UIViewController* targetVC = messageController.viewControllers.firstObject;
// Check if the instance is of correct class type
if ([targetVC isKindOfClass:NSClassFromString(#"CKSMSComposeController")]) {
SEL sel1 = NSSelectorFromString(#"setCanEditRecipients:");
if ([targetVC respondsToSelector:sel1]) {
// put breakpoint here to check whether this line is executed
[targetVC performSelector:sel1 withObject:#NO];
}
SEL sel2 = NSSelectorFromString(#"setTextEntryContentsVisible:");
if ([targetVC respondsToSelector:sel2]) {
// put breakpoint here to check whether this line is executed
[targetVC performSelector:sel2 withObject:#NO];
}
}
});
}];
}
}

objective c calling method inside own instance

Really new with objective c (building iphone app) and I'm trying to figure out how to properly understand how calling methods work (compared to c#, the most recent language I've been working with)
I have this implementation
#interface User : NSObject{
}
#property NSInteger Id;
#property NSString *email, *password;
-(BOOL)isValid;
#end
#implementation User
-(BOOL)isValid{
NSString *password = self.validateString:self.password;
NSString *email = self.email;
if(validUser){
return YES;
}else{
return NO;
}
}
EDIT: SOrry if it wasn't clear but this is the method I'm trying to call.
-(NSString *)validateString:(NSString *)string{
// process the string
return #"";
}
Basically I'm trying to create an instance in my view onclick of a button like so:
- (IBAction)btnSubmit:(id)sender {
// get values of email and password
// do an isvalid to check with web service.
User *user = [[User alloc] init];
user.email = #"email#email.com";
user.rawPassword = #"pass";
if(user.isValid){
// go to next page
}
else{
// else refresh current page
}
}
Is creating the instance on click and then passwing the values to process inside the instance a good practice?
Thanks!
No need of creating it's own object there. You can use the current object for doing this:
- (IBAction)btnSubmit:(id)sender
{
self.email = #"email#email.com";
self.rawPassword = #"pass";
if(self.isValid)
{
// go to next page
}
else
{
// else refresh current page
}
}
You can refer to same object within its scope using self keyword. It's similar to this keyword used in C++
I'm not sure which methods you are trying to call within the object but to call a method in the current object you use self.
i.e.
[self runSomeFunction];
Use
Note:- If you are creating a button actiion in same class then there is no need to create an instance of same class , you can use self
- (IBAction)btnSubmit:(id)sender {
// get values of email and password
// do an isvalid to check with web service.
User *user = [[User alloc] init];//not need if its same user class
user.email = #"email#email.com";//self.email=#"email#email.com"; if same user class
user.rawPassword = #"pass";//self.rawPassword=#"pass"; if same user class
if([self isValid]){//made a change here as in objective c its a syntax to call a method not with "."
// go to next page
}
else{
// else refresh current page
}
}
That depens on whether you really need to alloc a new instance which we cannt say because that depends on your business logic.
If you would crate a new instance in c++ here with new, then yes, alloc/init a new instance and use it.
If you would refer to this in c++, as your questiont title suggests, then you can use self quite corresponding to this. self.isValid and [self isValid] are equivalents.
This is for instance methods. Within class methods self would refer to the class, not the instance

Testing NSWidowController using OCMock

I've been trying to come up with a a way to unit test my applicationDidFinishLaunching delegate using OCMock. My NSWindowController is instantiated here and I'd like to test it. Here's my test code:
id mockWindowController = [OCMockObject niceMockForClass:[URLTimerWindowController class]];
[[mockWindowController expect] showWindow:self];
NSUInteger preRetainCount = [mockWindowController retainCount];
[appDelegate applicationDidFinishLaunching:nil];
[mockWindowController verify];
When I run the test, I get the error:
"OCMockObject[URLTimerWindowController]: expected method was not invoked: showWindow:-[URLTimerAppDelegateTests testApplicationDidFinishLaunching]"
The log gives more detail:
"Test Case '-[URLTimerAppDelegateTests testApplicationDidFinishLaunching]' started.
2011-04-11 08:36:57.558 otest-x86_64[3868:903] -[URLTimerWindowController loadWindow]: failed to load window nib file 'TimerWindow'.
Unknown.m:0: error: -[URLTimerAppDelegateTests testApplicationDidFinishLaunching] : OCMockObject[URLTimerWindowController]: expected method was not invoked: showWindow:-[URLTimerAppDelegateTests testApplicationDidFinishLaunching]
Test Case '-[URLTimerAppDelegateTests testApplicationDidFinishLaunching]' failed (0.005 seconds).
"
So I see that the NIB fails to load. Ok, so how do I make it load while unit testing or somehow mock its load? I've already looked at the OCMock docs, Chris Hanson's tips on unit testing and a few other resources, including the WhereIsMyMac source code which behave in a similar fashion. My application for instantiating the window controller is this:
self.urlTimerWindowController = [[URLTimerWindowController alloc] init];
[self.urlTimerWindowController showWindow:self];
Any tips greatly appreciated.
The problem with your test is that mockWindowController and urlTimerWindowController are not the same object. And self in your test is not the same as self in the class under test. It doesn't really matter that the nib doesn't load in this case.
You generally can't mock an object when it's instantiated inside the method you want to test. One alternative is to instantiate the object in one method, then pass it to another method that finishes the setup. Then you can test the setup method. For example:
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.urlTimerWindowController = [[URLTimerWindowController alloc] init];
[self setUpTimerWindow:urlTimerWindowController];
}
-(void)setUpTimerWindow:(URLTimerWindowController *)controller {
[controller showWindow:self];
}
Then, you would test setUpTimerWindow::
-(void)testSetUpTimerWindowShouldShowWindow {
URLTimerAppDelegate *appDelegate = [[URLTimerAppDelegate alloc] init];
id mockWindowController = [OCMockObject niceMockForClass:[URLTimerWindowController class]];
[[mockWindowController expect] showWindow:appDelegate]; // this seems weird. does showWindow really take the app delegate as a parameter?
[appDelegate setUpTimerWindow:mockWindowController];
[mockWindowController verify];
[appDelegate release];
}

Passing along Methods - Objective-C

I'm fairly new to Objective-C, and it would be really helpful if someone could help me with the following task:
I have a class TheController that has a method DoTask. The goal of DoTask is to reach out to a MasterUtility (also a custom made class) and get Data, and then send it back when it is done (it uses a thread). Specifically, I want it to send it to dataReceiver in ReportsViewController. I think I need to use #selector or something like that. Here is some code:
#implementation ReportsViewController
-(void)doTask {
MasterUtilities *mu = [[MasterUtilities alloc] init];
[mu getDataAndSendTo:[WHAT GOES HERE]]
}
-(void)dataReceiver:(NSArray *)data {
NSLog(#"data: %#",data);
}
#end
Here is MasterUtilities
#implementation MasterUtilities
- (void)getDataAndSendTo:[WHAT GOES HERE] {
NSArray *data = [[NSArray init] alloc];
....getting data here....
[WHAT GOES HERE? HOW DO I CALL THE METHOD (dataReceiver) IN ReportsViewController?]
}
#end
Can anyone fill in the areas that indicate "WHAT GOES HERE"? Thank you!!
You could use a block:
typedef void (^Callback)(NSArray*);
[somebody doSomethingAndPerform:^(NSArray *data) {
// do whatever you want with the data
}];
- (void) doSomethingAndPerform: (Callback) callback
{
NSArray *data = …;
callback(data);
}
This is very flexible, but maybe too complex. If you want something simpler, you can always just pass the selector and target, just as you thought:
[somebody doSomethingAndCall:#selector(dataReceiver:) on:self];
- (void) doSomethingAndCall: (SEL) selector on: (id) target
{
NSArray *data = …;
[target performSelector:selector withObject:data];
}
Or you can use a protocol:
#protocol DataConsumer
- (void) handleData: (NSArray*) data;
#end
// this class has to implement DataConsumer
[somebody doSomethingAndNotify:self];
- (void) doSomethingAndNotify: (id <DataConsumer>) consumer
{
NSArray *data = …;
[consumer handleData:data];
}
This solution is a bit heawyweight, but the advantage is that the compiler catches some errors for you. There’s also more coupling, but it’s far from being a problem.
You have to use the Target-Action design pattern, which is widely used in Cocoa.
Good luck!
You may wish to reconsider how you approach this problem.
Rather than trying to get your MasterUtilities instance to send the data to your other method, why not have your getData method return the data from the method and then have your ReportsViewController pass the data to dataReciever: ?

OCMock on a method with argument and returns a value

I have a class that relies on NSUserDefaults that I'm trying to unit-test and I'm providing the NSUserDefaults as a mock into my test class. When running the test, I get the error:
OCMockObject[NSUserDefaults]:
unexpected method invoked:
dictionaryForKey:#"response"
I'm trying to mock this instance method of the NSUserDefaults class:
- (NSDictionary *)dictionaryForKey:(NSString *)defaultName;
using the call format:
[[[mockClass stub] andReturn:someDictionary] dictionaryForKey:#"aKey"]
to tell the mock that it needs to expect the dictionaryForKey method. But somehow, this isn't recorded or isn't the right thing to tell the mock to expect since the error tells me that mock never knew to expect the 'dictionaryForKey' call.
The way I'm invoking the stub's andReturn seems very similar to this question but in that one, the poster says they're getting a return value.
My test case:
-(void)testSomeWork
{
id userDefaultsMock = [OCMockObject mockForClass:[NSUserDefaults class]];
MyClass *myClass = [[MyClass alloc] initWith:userDefaultsMock];
NSDictionary *dictionary = [NSDictionary dictionary];
[[[userDefaultsMock stub] andReturn:dictionary] dictionaryForKey:#"response"];
BOOL result = [myClass doSomeWork];
STAssertTrue(result, #"not working right");
[myClass release];
[userDefaultsMock verify];
}
My class:
#implementation MyClass
#synthesize userDefaults;
- (id)initWith:(NSUserDefaults *aValue)
{
if (self = [super init])
{
self.userDefaults = aValue;
}
return self;
}
- (BOOL)doSomeWork
{
NSDictionary *response = [userDefaults dictionaryForKey:#"response"];
if (response != nil)
{
// some stuff happens here
return YES;
}
return NO;
}
#end
Any suggestions?
Not sure if you figured this out but it's probably to do with using stub with verify. You should use verify with expect.
i.e.
[[[userDefaultsMock expect] andReturn:dictionary] dictionaryForKey:#"response"];
...
[userDefaultsMock verify];
In this instance you use verify to confirm that your method did in fact call the expected method (dictionaryForKey:). You use stub to allow your method to call a given method on a Mock Object but you don't need to verify that it was called.