Why is my NSDate mock resulting in failed calls to [NSProxy methodSignatureForSelector:]? - objective-c

I'm trying to establish a pattern where I mock class methods of objects I don't own by creating a "fake" version of the class, then swizzling the orignal version to call the "fake" version's class methods and having the fake class call instance methods on a shared object which is itself mocked.
For example:
#interface FakeNSURLConnection : NSURLConnection
+ (void)enableMock:(id)mock;
+ (void)disableMock;
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
- (BOOL)canHandleRequest:(NSURLRequest *)request;
#end
#implementation FakeNSURLConnection
...
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate {
return [FakeNSURLConnection.sharedInstance connectionWithRequest:request delegate:delegate];
}
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate { return nil; }
+ (BOOL)canHandleRequest:(NSURLRequest *)request { return [FakeNSURLConnection.sharedInstance canHandleRequest:request]; }
- (BOOL)canHandleRequest:(NSURLRequest *)request { return NO; }
#end
What happens is that my enable mock swaps the class methods of NSURLConnection and FakeNSURLConnection and make the "sharedInstance" a mock of FakeNSURLConnection. In this way, when my mock is enabled a call like [NSURLConnection canHandleRequest:request] will be directed to my mock method.
This gets a little trickier with NSDate, I think because it is an "abstract" class that does toll-free bridging, but I'm not sure. I thought I could get maximum benefit out of this model by having my Fake class return real values if I wanted to partially mock an object. So we have something like this:
#interface FakeNSDate : NSDate
...
- (id)date;
- (id)dateWithTimeInterval:(NSTimeInterval)seconds sinceDate:(NSDate *)date;
- (id)dateWithTimeIntervalSince1970:(NSTimeInterval)seconds;
- (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)seconds;
- (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)seconds;
- (id)distantFuture;
- (id)distantPast;
- (NSTimeInterval)timeIntervalSinceReferenceDate;
#end
#implementation FakeNSDate
...
+ (id)date { return [FakeNSDate.sharedInstance date]; }
- (id)date { return [[NSDate alloc] init]; }
...
#end
Not that I am not swizzling alloc, in fact I'm only swizzling the publicly available class methods as listed in the API for NSDate.
I'm using this code:
_baseDate = [NSDate dateWithTimeIntervalSinceNow:-1.0 * 90.0 * 60.0];
_nsDateMock = [OCMockObject partialMockForObject:[FakeNSDate sharedInstance]];
NSLog(#"_nsDateMock %#", [_nsDateMock class]);
[FakeNSDate enableMock:_nsDateMock];
[[[_nsDateMock stub] andReturn:_baseDate] date];
NSLog(#"DATE: %#", [NSDate date]);
This code executes properly (the date reports being 1.5 hours ago) but I get an error that pops up: [NSProxy methodSignatureForSelector:] called!
Is the issue something I'm doing or something that partialMockForObject does? This pattern I'm using seems very helpful -- I'd hate to think it's a dead end.

I did two things to make my problems go away:
_nsDateMock = [OCMockObject partialMockForObject:[FakeNSDate sharedInstance]];
I must not have been unsetting this correctly and, I think, re-partial-mocking and already partial-mocked object. I changed it to just alloc a new object:
_nsDateMock = [OCMockObject partialMockForObject:[[FakeNSDate alloc] init]];
I also switched to using the latest code off of github.

Related

CMPedometer as property, Error code 103

I'm building a step manager class, and in my .m file, I've added CMPedometer as a property:
#interface WDStepTrackerManager ()
#property (nonatomic, strong) CMPedometer *pedometer;
#end
I then instantiate it in my init method (I've also tried lazy instantiation -- no effect):
- (instancetype)init
{
self = [super init];
if (self) {
self.pedometer = [[CMPedometer alloc] init];
}
return self;
}
Finally, I try to use the pedometer like so:
- (void)stepsForTodayWithCompletion:(void(^)(int steps))completion
{
[self.pedometer queryPedometerDataFromDate:[[NSDate date] midnight] toDate:[NSDate date] withHandler:^(CMPedometerData *pedometerData, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (pedometerData) {
completion([pedometerData.numberOfSteps intValue]);
}
});
}];
}
Every time, however, despite approving access to the data, I receive a 103 error and pedometerData is nil. (If you're wondering about the midnight selector on NSDate, it's from a category I built which has tons of unit tests and works correctly, as far as I can tell.) Other questions have said making CMPedometer a property of your class should stop this from occurring, but I'm still receiving the error despite doing exactly that.
What am I doing wrong? How can I change my (very simple) code to get the steps?
The issue was that, despite having a strong pointer to my CMPedometer instance, I didn't have a strong pointer to my WDStepTrackerManager wrapper class. Instantiating this class and storing it in a strong property solved my problem.
I had the same problem, but instead of making it a property i made it an instance variable, so try this instead
#implementation WDStepTrackerManager
CMPedometer *pedometer;
- (instancetype)init
{
self = [super init];
if (self)
{
pedometer = [[CMPedometer alloc]init];
}
return self;
}
And use it like this
- (void)stepsForTodayWithCompletion:(void(^)(int steps))completion
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfDate =[calendar startOfDayForDate:[NSDate date]];
[pedometer queryPedometerDataFromDate:startOfDate toDate:[NSDate date] withHandler:^(CMPedometerData *pedometerData, NSError *error) {
if (pedometerData) {
completion([pedometerData.numberOfSteps intValue]);
}
});
}];
}

Factory methods in Objective-C

I make class factories like so,
#implementation Universe {
NSString *foo;
}
+ (instancetype)universeWithMeaning:(NSString *)meaning
{
return [[self alloc] initUniverseWithMeaning:meaning];
}
- (id)initUniverseWithMeaning:(NSString *)meaning
{
if (self = [super init]) {
foo = meaning;
}
return self;
}
- (void)showMeaning
{
NSLog(#"%#", foo);
}
#end
And create object like this,
Universe *universe = [Universe universeWithMeaning:#"42"];
[universe showMeaning]; // Prints 42
This works great, but the method signature of initUniverseWithMeaning: is the same as that of universeWithMeaning:, except that it's an instance method which allows it to save instance variables to the created object.
Is there a way to this without having to implement the initUniverseWithMeaning: instance method?
I know its necessary to be inside of an instance method to be able to access instance variables, so I've been experimenting with blocks. My idea was to pass a block containing instance variable assignations to the class method which would somehow execute it in the instance context.
Implementation,
#implementation Cat {
NSString *lives;
}
+ (Cat *)newCat:(void(^)(void))cat
{
cat(); // **Problem 1**
}
- (void)showLives
{
NSLog(#"%#", lives);
}
#end
Usage,
Cat *cat = [Cat newCat:^void (void) {
self.lives = 9; // **Problem 2**
}];
[cat showLives]; // I'd like this to print 9
Problem 1: How to create a Cat object and execute cat() inside it?
Problem 2: How to make self refer to the object in the block's execution environment?
Anyway, this is more of a curiosity than anything else, it's would only be practically useful to save me from writing alloc (I would just need to include a method prototype for initUniverseWithMeaning: in the .h file.)
For your problem 1 and 2, you can try this
#interface Cat ()
#property (strong) NSString *lives;
#end
#implementation Cat
+ (Cat *)newCat:(void(^)(Cat *me))cat
{
Cat *newcat = [[self alloc] init];
cat(newcat);
return newcat;
}
- (void)showLives
{
NSLog(#"%#", lives);
}
#end
Cat *cat = [Cat newCat:^(Cat *me) {
me.lives = 9;
}];
[cat showLives]; // print 9
but I can't see much use of it... Isn't this simpler?
Cat *cat = [Cat new];
cat.lives = 9;
[cat showLives];
For your real problem
Is there a way to this without having to implement the initUniverseWithMeaning: instance method?
+ (instancetype)universeWithMeaning:(NSString *)meaning
{
Universe *universe = [[self alloc] init];
if (universe) universe->foo = meaning;
return universe;
}
The first example you've posted is the correct way of creating Objective-C factory methods.
An Objective-C factory method is nothing more than a class method wrapper around an instance level init method. Generally speaking, every factory method should have a paired init method that takes the same number and type of arguments.
fooWithBar:(NSString *)bar should be paired with initWithBar:(NSString *)bar, etc.
An exception might come in when you have an init method that takes arguments, but you've create a handful of factory methods with default arguments for this method. For example:
- (instancetype)initWithString:(NSString *)string;
+ (instancetype)fooWithString:(NSString *)string {
return [[self alloc] initWithString:string];
}
+ (instancetype)fooWithBar {
return [[self alloc] initWithString:#"bar"];
}
Now, you can create the object with in the method, then modify it, and return the modified object.
For example:
+ (instancetype)fooWithString:(NSString *)string {
Foo *f = [[self alloc] init];
f.str = string;
return f;
}
But honestly, it's just better to have an initWithString: method.
Every class should have a designated initializer and every object of that class should go through the designated initializer.

NSURLSession delegation: How to implement my custom SessionDelegate class accordingly?

Got a singleton class, so called RequestManager, which shall handle requests made by different modules and background tasks of my application.
#interface RequestFactory : NSObject
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender;
...
#end
Then I got another class, so called SessionDelegate, which shall handle all the callbacks during the request.
#interface SessionDelegate : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
#property (weak, nonatomic) id <RequestFactoryDelegate> delegate;
#end
My idea is to encapsulate the functions in these classes to not overload my classes, because I need a lot of helper classes with CommonCrypto and so on.
So I set quickly coded a protocol RequestFactoryDelegate to send the received data to the sender who initiated the origin request.
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
self.sessionDelegate.delegate = sender;
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
[dataTask resume];
}
Well, it works if I have an object, let us call it senderA which sends the requests, because the set delegate is always senderA itself.
The problem occurs having another object, e.g. senderB which sends requests - not even at the same time - but very shortly after senderA send.
- (void)foo
{
[requestFactory requestDataWith:token
id:id
sender:senderA]; // let's assume this takes 20s
[requestFactory requestDataWith:token
id:id
sender:senderB]; // let's assume this takes 1s
}
Because the request of senderA is still in progress, senderB sets the delegate to him and what happens is the delegate function of senderB is run twice.
<senderB>
<senderB>
Well... I really need to implement an own custom delegate (whether or not in the same class as the RequestFactory or not), but how to I handle the callback methods so I can respond properly to either senderA or senderB?
My last idea is to override the NSURLSessionTasks class and implement an own delegate property or block property or whatever.
Many thanks in advance.
You can attach an arbitrary object to a task:
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
id whatever = // anything at all!
[NSURLProtocol setProperty:whatever forKey:#"thing" inRequest:req];
NSURLSessionDownloadTask* task = [[self session] dataTaskWithRequest:req];
And retrieve it later (in the delegate) like this:
NSURLRequest* req = task.originalRequest;
id thing = [NSURLProtocol propertyForKey:#"thing" inRequest:req];
The value here (whatever) can be any object. It can be a completion handler callback - I've done this and it works just fine.
In Objective-C, there is an alternative to subclassing that might be what you want here: associating objects.
It works like this: you can "attach" (associate) an object to another object with a custom key and later retrieve it. So in your case, you would do something like:
#include <objc/runtime.h>
// Top level of your .m file. The type and content of this
// variable don't matter much, we need the _address_ of it.
// See the first link of this answer for details.
static char kDelegateKey = 'd';
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
// Associate the sender with the dataTask. We use "assign" here
// to avoid retain cycles as per the delegate pattern in Obj-C.
objc_setAssociatedObject(dataTask, &kDelegateKey, sender, OBJC_ASSOCIATION_ASSIGN);
[dataTask resume];
}
- (void)someOtherMethodWithDataTask:(NSURLSessionDataTask *)dataTask
{
// Read the attached delegate.
id<RequestFactoryDelegate> delegate = objc_getAssociatedObject(dataTask, &kDelegateKey);
// Do something with the delegate.
}
Here is my solution.
I just just use the unique identifier of each sessionTask object. So my delegate object contains a dictionary with blocks as values to execute on success/failure and the identifier as keys to identify the correct execution block.
In the .h file I declared the dictionary and a method to add a key/value object:
#property (nonatomic, strong) NSDictionary *completionHandlerDictionary;
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString *)identifier;
And in the .m file I call the handler block.
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString*)identifier
{
if ([self.completionHandlerDictionary objectForKey:identifier]) {
NSLog(#"Error: Got multiple handlers for a single task identifier. This should not happen.\n");
}
[self.completionHandlerDictionary setObject:handler forKey:identifier];
}
- (void)callCompletionHandlerForTask:(NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
if (handler) {
[self.completionHandlerDictionary removeObjectForKey: identifier];
NSLog(#"Calling completion handler.\n");
handler();
}
}
That's it, simple as it is.

Can I pass delegate as a parameter objective-c

I am working with an NSOperationQueue and I want to add new NSOperations to the NSOperationQueue. It is a queue that lives in a singleton instance of a class I have. It would make things a lot easier if I could move everything into the static class by passing the delegate.
Here is my code now as it lives in - this is in a cellForRowAtIndexPath
NSString *key = [NSString stringWithFormat:#"%#%#",cell.dataItem.ItemID, cell.dataItem.ManufacturerID];
if (![self.imgOperationInQueue valueForKey:key]) {
ImageOperation *imgOp = [[ImageOperation alloc] initWithItemID:cell.dataItem.ItemID withManufacturerID:cell.dataItem.ManufacturerID withReurnType:kThumbnail];
imgOp.identifier = [NSString stringWithFormat:#"%i", cell.tag];
imgOp.delegate = self;
[[SharedFunctions sharedInstance] addImageOperationToQueue:imgOp];
[imgOp release];
// store these in the dictionary so we don;t queue up requests more than once
[self.imgOperationInQueue setValue:cell.dataItem.ItemID forKey:key];
}
If I could add the delegate as a parameter I could put all of this code into the shared singleton class and call it from anywhere in my app.
I suppose that I could use an NSNotification - or can I use a block of some sort?
Just create the appropriate init method that passes in the delegate.
- (id)initWithItemID:(NSString *)itemID
withManufacturerID:(NSString *)manufacturerID
withReurnType:(NSInteger)type
delegate:(id<YourDelegate>)theDelegate
{
self = [super init];
if (self)
{
.... // Other assignments
self.delegate = theDelegate;
}
return self;
}

How can i unit test an object internal to a method in Objective-C?

I'm wondering how to go about testing this. I have a method that takes a parameter, and based on some properties of that parameter it creates another object and operates on it. The code looks something like this:
- (void) navigate:(NavContext *)context {
Destination * dest = [[Destination alloc] initWithContext:context];
if (context.isValid) {
[dest doSomething];
} else {
// something else
}
[dest release];
}
What i want to verify is that if context.isValid is true, that doSomething is called on dest, but i don't know how to test that (or if that's even possible) using OCMock or any other traditional testing methods since that object is created entirely within the scope of the method. Am i going about this the wrong way?
You could use OCMock, but you'd have to modify the code to either take a Destination object or to use a singleton object which you could replace with your mock object first.
The cleanest way to do this would probably be to implement a
-(void) navigate:(NavContext *)context destination:(Destination *)dest;
method. Change the implementation of -(void) navigate:(NavContext *)context to the following:
- (void) navigate:(NavContext *)context {
Destination * dest = [[Destination alloc] initWithContext:context];
[self navigate:context destination:dest];
[dest release];
}
This will allow your tests to call the method with an extra parameter directly. (In other languages, you would implement this simply by providing a default value for the destination parameter, but Objective-C does not support default parameters.)
What i want to verify is that if context.isValid is true, that doSomething is called on dest
I think you may be testing the wrong thing here. You can safely assume (I hope) that boolean statements work correctly in ObjC. Wouldn't you want to test the Context object instead? If context.isValid then you're guaranteed that the [dest doSomething] branch gets executed.
It's completely possible, using such interesting techniques as method swizzling, but it's probably going about it the wrong way. If there's absolutely no way to observe the effects of invoking doSomething from a unit test, isn't the fact that it invokes doSomething an implementation detail?
(If you were to do this test, one way to accomplish your aims would be replacing the doSomething method of Destination with one that notifies your unit test and then passes on the call to doSomething.)
I like to use factory methods in this situation.
#interface Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context;
#end
#implementation Destination(Factory)
+ (Destination *)destinationWithContext:(NavContext *)context
{
return [[Destination alloc] initWithContext:context];
}
#end
I then make a FakeClass:
#import "Destination+Factory.h"
#interface FakeDestination : Destination
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
// Note! Instance method!
- (Destination *)destinationWithContext:(NavContext *)context;
#end
#implementation FakeDestination
+ (id)sharedInstance
{
static id _sharedInstance = nil;
if (!_sharedInstance)
{
_sharedInstance = [[FakeDestination alloc] init];
}
return _sharedInstance;
}
+ (void)setSharedInstance:(id)sharedInstance
{
_sharedInstance = sharedInstance;
}
// Overrides
+ (Destination *)destinationWithContext:(NavContext *)context { [FakeDestination.sharedInstance destinationWithContext:context]; }
// Instance
- (Destination *)destinationWithContext:(NavContext *)context { return nil; }
#end
Once you set this up, you just need to swizzle the class methods for + (Destination *)destinationWithContext:(NavContext *)context;
Now you're set to:
id destinationMock = [OCMock mockForClass:FakeDestination.class];
// do the swizzle
[FakeDestination setSharedInstance:destinationMock];
[[destinationMock expect] doSomething];
// Call your method
[destinationMock verify];
This is a fair amount of coding up front, but it's very reusable.