Dependency inject Class with class methods - objective-c

I have a class that has only class methods
//FileManager.h
+(void)writeData:(NSData*)data toFile:(NSString*)filePath;
//...
//some more class methods
I use this class inside another one
//AttachmentsRepository.m
-(void)newAttachment:(NSData*)attachmentData{
//some code
NSString * generatedPath = #"/some/generated/path";
[FileManager writeData:attachmentData toFile:generatedPath];
}
Now I am writing tests for AttachmentRepository and was wondering how to inject FileManager.
What I use is the "extract and override call" technique:
//AttachmentsRepository.m
-(void)newAttachment:(NSData*)attachmentData{
//some code
NSString * generatedPath = #"/some/generated/path";
[[self getFileManagerClass] writeData:attachmentData toFile:generatedPath];
}
-(Class)getFileManagerClass{
return [FileManager class];
}
Now in my test I just stub out getFileManagerClass to return [FakeFileManager class]
What is a better solution? This is legacy code but lets say that we are writing it now. How should the FileManager dependency be implemented.
PS. I want to use a fake because I would like to avoid writing to disk when testing the AttachmentRepository class.

From "Working Effectively With Legacy Code" by Michael C. Feathers I found out about the "Introduce Instance Delegator" technique.
Basically we duplicate the methods we need and make them instance methods. Their body is just a call to the class/static method. Then we use the instance class as argument to the constructor or the method that needs to use it.
//FileManager.h
+(void)writeData:(NSData*)data toFile:(NSString*)filePath;
-(void)writeData:(NSData*)data toFile:(NSString*)filePath;
//...
//FileManager.m
-(void)writeData:(NSData*)data toFile:(NSString*)filePath;{
[FileManager writeData:data toFile:filePath];
}
In AttachmentsRepository:
//AttachmentsRepository.m
-(instance) initWithFileManager:(FileManager *)fileManager{
if(self = [super init]){
_fileManager = fileManager
}
return self;
}
-(void)newAttachment:(NSData*)attachmentData{
//some code
NSString * generatedPath = #"/some/generated/path";
[self.fileManager writeData:attachmentData toFile:generatedPath];
}
The conclusion seems to be that the FileManager class shouldn't have had class/static methods but instance methods instead when it was implemented at the start.
All this kinda makes me feel awkward - making methods that can be class/static to be instead on instance level.

Related

Class issues: should I use everywhere self as class in objective c?

Wow, great issue I have found for myself.
What is it? The candy or the garlic?
something about Objective-C:
Are there any issues not to use 'self' in (+) - class methods as class?
in the deep of a class...
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
Ruby here:
For example, in Ruby everything is object and class is object of class Class and there is a good practice to rely on self:
class DateClass
# self is DateClass here, inside of class definition, uh
self.dateWithTimeInterval(interval)
self.dateWithTimeIntervalSince1970(interval)
end
end
Perl here:
Another example was found in perl oop deep: (thanks for this thread)
sub new {
my $proto = shift || die "Must pass a class or object into new()";
my $class = ref($proto) || $proto;
bless {}, $class;
}
So, in Perl and in Ruby guys always rely on $class refs
Maybe example with Perl code not obvious, but it happens all time. Programmers rely on $class reference and take class name with it. also, they can invoke some methods with it:
my $class = 'Class';
$class->new();
or
Class::->new()
After all...
Which pitfalls or caveats could you provide against usage self as class in objective-c?
Usually you use self whenever you can but of course, there are situations when referencing the class by [MyClass class] is desired. Almost all of the scenarios are related to inheritance.
For example, a creator method for a class A.
#implementation A
+ (id)createInstanceWithParam:(NSInteger)param {
return [[self alloc] initWithParam:param];
}
#end
Will work correctly even if we create a subclass B. However, if we decide to implement a class cluster, then we have to reference classes by names:
#implementation SomeDataStructure
+ (id)createInstanceWithType:(NSInteger)type {
if (type == 0) {
return [[DataStructureImpl1 alloc] init];
}
else if (type == 1) {
return [[DataStructureImpl2 alloc] init];
}
}
#end
Another example is the common example of +initialize
+ (void)initialize {
if (self == [MyClass class]) {
...perform initialization...
}
}
And of course, if you are overriding a method, then using self or using [MySelf class] can be a distinction between your overriden implementation and the original implementation. Although super could be used there, too.
TLDR:
self is preferred but be careful with subclasses/superclasses.
For understanding pros and cons of using self vs. class name let's consider one situation:
Class A is subclass of NSDate and implements method +(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval.
Class B is subclass of A and overrides implementation of +dateWithTimeIntervalSince1970:(NSTimeInterval)interval method that declared in NSDate.
Now let's consider two possible implementations of +(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval method in A:
1. Using self
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
if run [B dateWithTimeInterval:interval]; then self in above code is kind of B class and as expected custom implementation (in class B) for +(NSDate*)dateWithTimeIntervalSince1970:(NSTimeInterval)interval method would be called.
2. Using directly NSDate
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [NSDate dateWithTimeIntervalSince1970:interval];
}
if run [B dateWithTimeInterval:interval]; then overridden implementation (in class B) would be ignored and instead of it: original implementation (in class NSDate) for +(NSDate*)dateWithTimeIntervalSince1970:(NSTimeInterval)interval method would be called. It's so because we directly send message to NSDate: [NSDate dateWithTimeIntervalSince1970:interval];.
This behavior is unexpected for developer.
For the same reason declare methods in such way:
+(instancetype)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
By using instancetype compiler will know what kind of object is returned by method-initializer. When you call [B dateWithTimeInterval:interval] it returns object of kind B but not NSDate.

How to mock an object with OCMock which isn't passed as a parameter to method?

I have a method I would like to test with OCMock but not sure how to do that. I need to mock
ExtClass which isn't defined as part of my code (external library):
+(NSString *)foo:(NSString *)param
{
ExtClass *ext = [[ExtClass alloc] initWithParam:param];
if ([ext someMethod])
return #"A";
else
return #"B";
}
Thanks in advance!
OCMock 2
id mock = [OCMockObject mockForClass:[ExtClass class]];
// We stub someMethod
BOOL returnedValue = YES;
[[[mock stub] andReturnValue:OCMOCK_VALUE(returnedValue)] someMethod];
// Here we stub the alloc class method **
[[[mock stub] andReturn:mock] alloc];
// And we stub initWithParam: passing the param we will pass to the method to test
NSString *param = #"someParam";
[[[mock stub] andReturn:mock] initWithParam:param];
// Here we call the method to test and we would do an assertion of its returned value...
[YourClassToTest foo:param];
OCMock3
// Parameter
NSURL *url = [NSURL URLWithString:#"http://testURL.com"];
// Set up the class to mock `alloc` and `init...`
id mockController = OCMClassMock([WebAuthViewController class]);
OCMStub([mockController alloc]).andReturn(mockController);
OCMStub([mockController initWithAuthenticationToken:OCMOCK_ANY authConfig:OCMOCK_ANY]).andReturn(mockController);
// Expect the method that needs to be called correctly
OCMExpect([mockController handleAuthResponseWithURL:url]);
// Call the method which does the work
[self.myClassInstance authStarted];
OCMVerifyAll(mockController);
Notes
Ensure that in both cases you stub two methods (alloc and the init... method). Also, make sure that both stubbing calls are made on the instance of the class mock (not the class itself).
Docs: Class methods section in the OCMock features
Alternatives
This (strange)solution may be useful in case you want to test legacy code that due to whatever reason you cannot refactor. However, if you can modify the code you should refactor it and get an ExtClass object as a parameter, not a string, delegating the creation of ExtClass out of that method. Your production and test code would be simpler and clearer, specially in a more complex real life case, not in this simple example.

Unit testing with OCMock and MagicalRecord

Although I have seen similar questions in SO, none of the answers seems to solve my problem.
I have a NSManagedObject class generated by mogenerator with custom functions (not in the model):
#interface MyManagedClass : _MyManagedClass {
-(NSNumber*)getRandomNumber2;
-(void)function_I_want_to_test;
}
My function_I_want_to_test() depend on the result of random(), and that is something I must control during testing. Since I cannot mock random() I built a function wrapper, which by the way, is not static because I had many problems with OCMock and static class functions.
The setup of my unit test looks like:
[MagicalRecord setDefaultModelFromClass:[self class]];
[MagicalRecord setupCoreDataStackWithInMemoryStore];
Using the debugger I could verify that the model is properly loaded. Also if I do it the non magical way:
NSBundle *b = [NSBundle bundleForClass:[self class]];
model = [NSManagedObjectModel mergedModelFromBundles:#[b]];
After this point I cannot create any mock to stub my random() wrapper function
I have tried a class mock
id mock = [OCMockObject mockForClass:[MyManagedClass class]];
[[[mock stub] andReturn:#50] getRandomNumber2];
MyManagedClass *my_object = [mock MR_createEntity];
I have tried using a partial mock
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
id mock2 = [OCMockObject partialMockForObject:my_object];
After last point, just creating an instance of mock2 destroys the dynamic properties of my_object, and it become useless.
I have also tried using a mock protocol with the function I want to stub, still to no avail.
The runtime exception is the normal one other people get when using with tests with Core Data objects: the properties are not recognized selectors.
However the strange thing to me is that I am not trying to stub any dynamic property, but a normal, compile time known function. Hence it seems strange to me that using OCMock renders my instances useless.
Ideally I would like something that uses OCMock/Mogenerator/Magicalrecord.
what am I doing wrong?
I recommend against trying to mock out managed objects. There's a lot of runtime craziness going on to make managed objects work. That's why I suggest going with an in memory database approach for testing. This will let you create empty instances of your entities while letting the core data stuff happen.
Since you're probably using unit tests, I suggest that in each test case where you think you need to mock out some data to instead recreate your whole stack, and set a new entity up with the state it needs to run your test. You can also make a test in memory persistent store separate from the one the default stack create method gives you, and attach this second one to your default stack. That is, make a new in memory store, initialize it with your fake/mock data entities, and attach that to your test data stack.
I hope this rambling helps a bit, but the bottom line is dont mock managed objects...really.
You could do it by moving your random number generation out of the Core Data object and into a helper class:
#implementation RandomNumberGenerator
static RandomNumberGenerator *_sharedInstance = nil;
+(RandomNumberGenerator *)sharedInstance {
if (_sharedInstance == nil) {
_sharedInstance = [[RandomNumberGenerator alloc] init];
}
return _sharedInstance;
}
+(void)setSharedInstance:(RandomNumberGenerator *)instance {
[instance retain];
[_sharedInstance release];
_sharedInstance = instance;
}
-(NSNumber *)generateRandomNumber {
return ...
}
#end
Then in your test:
-(void)testSomething {
id mockGenerator = [OCMockObject mockForClass:[RandomNumberGenerator class]];
[RandomNumberGenerator setSharedInstance:mockGenerator];
[[[mockGenerator stub] andReturn:#(4)] generateRandomNumber];
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
expect(my_object.someField).to.equal(someValueBasedOnGeneratedRandomNumber);
}

init] as a factory method

I want to initialize an instance of one of the subclasses of a superclass depending on the arguments to init:
[[Vehicle alloc] initWithItinerary: shortWay]; // returns a bicycle
[[Vehicle alloc] initWithItinerary: longWay]; // returns a car
I can't find examples of code like this. I wonder if this is not idiomatic in Objective C, or I simply am not looking in the right places.
You could do this via a custom init method, but it'd be kind of tedious (you'd have to invoke [super init], but then call [self release], etc...). It'd be much simpler to create a class method on Vehicle and use that as your factory method. For example:
+ (id) vehicleWithItinerary:(id)someItinerary {
if ([someItinerary isAShortWay]) {
return [[[Bicycle alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isAMediumWay]) {
return [[[RocketPack alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isALongWay]) {
return [[[Car alloc] initWithItinerary:someItinerary] autorelease];
}
return nil;
}
Look at [UIButton buttonWithType:] for an example of how Apple does this. Instead of init, they use a static method of the base class to allocate an instance of the appropriate derived class.
You can also pass around Class objects. Maybe the itinerary knows the Class or class name to allocate. You can do something like this:
[[[itinerary classToAllocate] alloc] initWithItinerary:itinerary];
or
[[NSClassFromString( [itinerary classNameToAllocate] ) alloc] initWithItinerary:itinerary];
You are allowed to release self and create a new object in init, although this is rarely used. Just watch out for recursion.
-(id) initWithItinerary:(Itinerary *)inItinerary {
[self release]; // super init never called - safe if you wrote super classes
self = [[[inItinerary classToAllocate] alloc] init];
self.itinerary = inItinerary;
return self;
}
This is called a class cluster. Several Cocoa classes work this way, including NSArray and NSString. The object returned from NSArray's init methods is never the same object that received the message. It's not that common outside of Cocoa, though, just because it's usually more complicated than people want to bother with. Basically, you figure out what actual class you want to use in your initializer, create an instance of that class, release yourself and return the other instance.
You might want to add an enum to the header file:
typedef enum {Bike, Car, JetPack
} vehicleType
That way your initWithItinerary: method can simply be:
if(VehicleType == Bike)
{
//do bike stuff
}
else if(VehicleType == Car)
{
//do car stuff
}
Why not have a method as part of the "way" that gives you a vehicle of the appropriate type for the way. e.g.
e.g.
// Somwhere before you use them. Car and Bicycle are subclasses of Vehicle
[shortWay setAppropriateVehicleType: [Bicycle class]];
[longWay setAppropriateVehicleType: [Car class]];
// when you need a vehicle
Vehicle* vehicle = [[[shortWay appropriateVehicleType] alloc] init];

Objective C : Given a Class id, can I check if this class implements a certain protocol? Or has a certain selector?

I want to use this for an object factory: Given a string, create a Class, and if
this Class supports a protocol (with a Create() method) then alloc the class and call
Create.
NSString *className; //assume this exists
Class class = NSClassFromString(className);
if ([class conformsToProtocol:#protocol(SomeProtocol)]) {
id instance = [[class alloc] init];
[instance create];
}
Class klass = NSClassFromString(classname);
if ([klass instancesRespondToSelector:#selector(create)]) {
[[klass alloc] create];
}
May I, however, point out just how many awful Objective-C rules you're breaking by doing the above? For example, you should never be calling methods on an allocated-but-not-initialized instance. The Xcode Static Analyzer will give you all sorts of warnings about memory leaks.
A better option would be this:
[[[klass alloc] init] create];
But you seem to imply that you don't want to call init.
You could consider a class method: [klass create], which would return a non-owned instance of klass. Then you'd just check [klass respondsToSelector:#selector(create)] before calling it.