Objective C - Unit testing & Mocking object? - objective-c

- (BOOL)coolMethod:(NSString*)str
{
//do some stuff
Webservice *ws = [[WebService alloc] init];
NSString *result = [ws startSynchronous:url];
if ([result isEqual:#"Something"])
{
//More calculation
return YES;
}
return NO;
}
I am using OCUnit
In the following method how can i mock my WebService Object, or the result to the method "startSynchronous" to be able to write an independent unit test?
Is it possible to inject some code in there to either create a mock web service or return a mock data on startSynchronous call?

One way is to use categories and override methods you want, you can even override the init method to return a mock object:
#interface Webservice (Mock)
- (id)init;
#end
#implementation Webservice (Mock)
- (id)init
{
//WebServiceMock is a subclass of WebService
WebServiceMock *moc = [[WebServiceMock alloc] init];
return (Webservice*)moc;
}
#end
The problem with this is that if you want to make the object return different results in different tests in 1 test file you cannot do that. (You can override each method once per test page)
EDIT:
This is an old question I posted, I thought I would update the answer to how I write testable code and unit test it nowadays :)
ViewController Code
#implementation MyViewController
#synthesize webService;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webService sendSomeMessage:#"Some_Message"];
}
- (WebService *)webService
{
if (!_webService)
_webService = [[WebService alloc] init];
return _webService;
}
#end
Test Code
#implementation MyViewControllerTest
- (void)testCorrectMessageIsSentToServer
{
MyViewController *vc = [[MyViewController alloc] init];
vc.webService = [OCMock niceMockForClass:[WebService class]];
[[(OCMockObject *)vc.webService expect] sendSomeMessage#"Some_Message"];
[vc view]; /* triggers viewDidLoad */
[[(OCMockObject *)vc.webService verify];
}
#end

Building on top of the WebService answer from aryaxt, here's a little trick to be able to get different results in different test.
First, you need a singleton object which will be used to store the desired answer, right before the test
TestConfiguration.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
void MethodSwizzle(Class c, SEL orig, SEL new);
#interface TestConfiguration : NSObject
#property(nonatomic,strong) NSMutableDictionary *results;
+ (TestConfiguration *)sharedInstance;
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector;
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector;
#end
TestConfiguration.m
#import "TestConfiguration.h"
void MethodSwizzle(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
#implementation TestConfiguration
- (id)init
{
self = [super init];
if (self) {
self.results = [[NSMutableDictionary alloc] init];
}
return self;
}
+ (TestConfiguration *)sharedInstance
{
static TestConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[TestConfiguration alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
[self.results setObject:result
forKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
return [self.results objectForKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
#end
Then you would define your "Mock" category to define mock methods , such as :
#import "MyWebService+Mock.h"
#import "TestConfiguration.h"
#implementation MyWebService (Mock)
-(void)mockFetchEntityWithId:(NSNumber *)entityId
success:(void (^)(Entity *entity))success
failure:(void (^)(NSError *error))failure
{
Entity *response = (Entity *)[[TestConfiguration sharedInstance] getResultForCallToObject:self selector:#selector(fetchEntityWithId:success:failure:)];
if (response == nil)
{
failure([NSError errorWithDomain:#"entity not found" code:1 userInfo:nil]);
}
else{
success(response);
}
}
#end
And finally, in the tests themselves, you would swizzle the mock method in the setup , and define the expected answer in each test, before the call
MyServiceTest.m
- (void)setUp
{
[super setUp];
//swizzle webservice method call to mock object call
MethodSwizzle([MyWebService class], #selector(fetchEntityWithId:success:failure:), #selector(mockFetchEntityWithId:success:failure:));
}
- (void)testWSMockedEntity
{
/* mock an entity response from the server */
[[TestConfiguration sharedInstance] setNextResult:[Entity entityWithId:1]
forCallToObject:[MyWebService sharedInstance]
selector:#selector(fetchEntityWithId:success:failure:)];
// now perform the call. You should be able to call STAssert in the blocks directly, since the success/error block should now be called completely synchronously.
}
Remark : in my example, the TestConfiguration uses class/selector as a key instead of object/selector. That means every object of the class will use the same answer for the selector. That is most likely your case, as webservice are often singleton. But it should be improved to an object/selector maybe using the objet's memory address instead of its class

Related

How does IMP work in iOS

In order to understand IMP, I wrote some test codes and found a problem in the process of testing.
I got a surprise 😂
Here is the code.
#interface Test : NSObject
#property (nonatomic, copy) NSString *name;
#end
#implementation Test
- (NSString *)description{
return [[super description] stringByAppendingString:self.name];
}
- (void)dealloc{
NSLog(#"Test dealloc %#",self);
}
- (void)test:(NSString *)str{
NSLog(#"- (void)test %#",str);
}
+ (void)test:(NSString *)str{
NSLog(#"+ (void)test %#",str);
}
- (void)test{
NSLog(#"- (void)test");
}
+ (void)test{
NSLog(#"+ (void)test");
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// IMP imp = [[Test new] methodForSelector:#selector(test:)];
// NSLog(#"%p",imp);
// IMP imp1 = [Test instanceMethodForSelector:#selector(test)];
IMP imp2 = [Test methodForSelector:#selector(test:)];
// NSLog(#"%p",imp2);
imp2();
NSLog(#"%p",imp2);
// imp2 = [Test methodForSelector:#selector(test)];
// imp2();
// imp();
// imp = [[Test new] methodForSelector:#selector(test:)];
// NSLog(#"%p",imp);
// imp();
// imp1();
// imp1();
// imp2();
// imp2();
// void (*func)(id,SEL,NSString *) = (void *)imp;
// func([Test class],#selector(test:),#"");
// void (*func1)(id,SEL,NSString *) = (void *)imp1;
// func1([Test class],#selector(test:),#"ha");
// void (*func2)(id,SEL,NSString *) = (void *)imp2;
// func2([Test class],#selector(test:),#"haha");
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
There's no problem when I run a single IMP.But when I run more than one IMP will crash for BAC_ACCESS and it only happens with the function which has arguments.So,why it can work with one and crash with two.
Of course you have a crash, because you need path id, SEL and also NSString * where needed.
Better cast to actual function prototype to have compiler errors when you call it wrong, instead of using direct IMP variables.
Apple should forbid () operator for IMPs and declare is as void * instead.
But people rarely use IMP for direct method calls, so there is no complains.

How do you create a global instance of a class in Objective-C

I am very new to Objective C and stumbled upon this problem.
How is it possible to create a global instance of a class in Objective C which is accessible from multiple classes and the main function?
You can do it in this way:
+(MyClass *) sharedInstance
{
static id sharedInstance = nil;
#synchronized(self)
{
if (!sharedInstance)
{
sharedInstance = [[MyClass alloc] init];
}
return sharedInstance;
}
}
Just access the object by this single line of code:
[MyClass sharedInstance]
Hope it helps!! Happy Coding :)
Add a method to your class that returns this specific instance.
The most common case is the singleton. You can also look up "shared instance".
You can use singleton, import the .h file in your .pch file, by doing that every file will have this imported :
YourClass.h :
#interface YourClass : NSObject
/**
* Method to get the singleton instance
* #return Instance of YourClass
*/
+ (YourClass *)instance;
#end
YourClass.m :
#import "YourClass.h"
#interface YourClass(){
}
#end
#pragma mark -
#implementation YourClass
- (id)init{
//prevents normal inits
return nil;
}
+ (YourClass *)instance
{
static YourClass *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] initSingelton];
});
return instance;
}
- (id)initSingelton{
if (self = [super init]) {
//do stuff here
return self;
}
return nil;
}

How to update gamehud in a CCSprite or CCNode, Cocos2d

I have a Gamehud where I want to display an object's name. There are lots of objects/sprites in main scene what I am trying to do is to display selected(on touch) objects' name on Gamehud.
Problem is if I alloc Gamehud in CCsprite class it creates new instance and does not update current Gamehud. If I use something like GameHUD *gamehud= (GameHUD *)[self.parent getChildByTag:99]; nothing happens I cannot send the object to GameHud class.
So what would be the correct way to update game hud in a ccsprite or ccnodeclass?
Main Scene;
-(id) init
{
if ((self = [super init]))
{
gameHud = [GameHUD gamehud];
[self addChild:gameHud z:2 tag:99];
}
}
My GameHud
+(id) gamehud
{
return [[self alloc] init];
}
-(id) init
{
if ((self = [super init]))
{
//bunch of labels
}
}
-(void)showName: :(Object *)obj
{
NSLog(#"Object name is %#", obj.name);
[_labelSpeed setString:obj.name];
}
In Object Class:CCSprite
-(void) onTouch
{
//obj is the object with name property that I want to use
GameHUD *gamehud= (GameHUD *)[self.parent getChildByTag:99]; // does not send the obj to gamehud and showName is not called
//GameHud *gamehud= [GameHud alloc] init]; // this displays nslog but doesnt update _label
[gamehud showName:obj];
}
First of all use a singlton or you will create a new GameHub everytime you call +(id) gamehud. i think this could be your problem: you add one GameHUD to the scene and call showName: of another object of GameHUD. Another problem is your -(id)init - you dont return self! so you never get your GameHUD
static GameHUD *sharedInstance = nil;
+(id) gamehud {
if( !sharedInstance ) {
sharedInstance = [[GameHUD alloc] init]
}
return sharedInstance;
}
-(id) init {
self = [super init];
if ( self ) {
//bunch of labels
sharedInstance = self;
}
return self; //i dont see this in your code!
}
from now you can access your hud from every point you want and you dont need to handle with tags. Be careful, its not the best way to create a Singleton (ask google). Dont call ..alloc] init] use only [GameHUD gamehud];
-(void) onTouch {
[[GameHUD gamehud] showName:obj];
}
Good Luck!
You may need to create a singleton or something like a semi singleton. Just add new nsobject class name "SingletonGameHud" to your app
SingletonGameHud.h
#import <Foundation/Foundation.h>
#import "GameHUD.h"
//create singleton class to use gamehud in movingobject class
#interface SingletonGameHud : NSObject
{
GameHUD *gamingHud;
}
#property(nonatomic,strong) GameHUD *gamingHud;
+(SingletonGameHud *)sharedInstance;
#end
SingletonGameHud.m
#import "SingletonGameHud.h"
#import "GameHUD.h"
#implementation SingletonGameHud
#synthesize gamingHud=_gamingHud;
+ (SingletonGameHud *)sharedInstance
{
static SingletonGameHud *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SingletonGameHud alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
_gamingHud = [GameHUD hud];
}
return self;
}
#end
in your game scene call
SingletonGameHud *sharedInstance= [SingletonGameHud sharedInstance];
hud = sharedInstance.gamingHud;
[self addChild:hud z:2 tag:99];
in your on touch method call
-(void) onTouch
{
SingletonGameHud *sharedInstance= [SingletonGameHud sharedInstance];
[sharedInstance.gamingHud showName:obj];
}

Dynamic link / Dynamic typing in objective-c

I have seen in the BNR 3rd edition an example to do a static singleton class.
To do that, they explain how to avoid a creation cycle calling the superclass alloc:
static MyClass *myclass = [[super alloc] init];
MyClass has its own init method.
NSObject -> MyClass
My doubt is: Which init class is sent? NSOject init, or MyClass init
Nested alloc init equals:
myclass = [super alloc] and then
myclass = [myclass init] ???????????
OR
myclass = [super alloc] and then myclass = [super init]
Do not use super, but do use self. Otherwise subclassing of your singleton will not work. The correct way is something like this:
+ (MyClass *)sharedInstance {
static MyClass *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
dispatch_once guarantees that your code (block in this case) is called only once. And self guarantees that subclassing will work properly.
for you question, the selector is always called on the subclass's implementation, which is your first guess
the way described in the book is a valid way to implement singleton, but i do not suggest it though. Robert Vojta's solution is good.
i cannot see how necessary to override allocWithZone, then you also need to make sure init does nothing (at least not leak)
Just for add some tests:
I have created 2 MyClass classes: NSObject -> Myclass -> My2ndClass
So:
#implementation Myclass
+(id) sharedClass {
static Myclass *miclase = nil;
miclase = [[self alloc] init];
NSLog(#"%#", [super description]);
return miclase;
}
-(id)init {
self = [super init];
NSLog(#"init de Myclass");
return self;
}
-(NSString *)description {
return #"i am Myclass";
}
#end
AND:
#implementation My2ndClass
+(id) sharedClass {
static My2ndClass *miclase = nil;
miclase = [[super alloc] init];
//miclase = [super init]; CRASH
NSLog(#"%#", [super description]);
return miclase;
}
-(id)init {
self = [super init];
NSLog(#"init de My2ndClass");
NSLog(#"%#", [super description]);
return self;
}
-(NSString *)description {
return #"i am My2ndClass";
}
#end
Then in AppDelegate:
Myclass *miclase = [Myclass sharedClass];
My2ndClass *mi2ndclase = [My2ndClass sharedClass];
This is the console output:
2012-09-03 17:18:55.742 Dynamic Typing[2891:207] init de Myclass
2012-09-03 17:18:55.744 Dynamic Typing[2891:207] Myclass
2012-09-03 17:18:55.746 Dynamic Typing[2891:207] init de Myclass
2012-09-03 17:18:55.747 Dynamic Typing[2891:207] init de My2ndClass
2012-09-03 17:18:55.748 Dynamic Typing[2891:207] i am Myclass
2012-09-03 17:18:55.751 Dynamic Typing[2891:207] My2ndClass
Like xlc0212 told, the correct messages when they are nested are:
miclase = [super alloc];
miclase = [miclase init];
Besides, if i do
miclase = [super alloc]
and then
miclase = [super init]
it CRASHES.
When is sent a class method (+) [super description], it logs class name (Myclass and My2ndClass). They are de class itself and don't have super object, do they?

NSObject inherited class crashes the application

I'm new in objective-C and here is my problem:
I want to write a game and i want to use a GameStateManager class to manage the game states. As i've read every class in Objective-C should be inherited from NSObject or it's subclass. So here is the GameStateManager interface:
#interface GameStateManager : NSObject {
int currentState_;
}
+(id) instance;
-(void) setState:(int)state;
#end
And here is the implementation:
#implementation GameStateManager
+(id) instance
{
GameStateManager *manager = [GameStateManager init];
return manager;
}
- (id) init
{
self = [super init];
return self;
}
- (void) setState: (int) state
{
switch (state)
{
case GS_MAIN_MENU_START:
{
// MenuScene *menuScene = [MenuScene node];
// [menuScene build: false];
MenuScene *scene = [scene instance:self :false];
[[CCDirector sharedDirector] runWithScene: scene];
}
break;
case GS_PLAYING:
{
}
break;
}
}
#end
I use this class here:
gameStateManager = [GameStateManager instance];
[gameStateManager setState: GS_MAIN_MENU_START];
The second line generated an SIGABRT signal. What's the problem ?
The problem is here:
+ (id) instance
{
GameStateManager *manager = [GameStateManager init];
return manager;
}
In effect you are calling init without ever calling alloc. I’d recommend that you forget about the instance stuff and use the official init patterns until you are really comfortable with the memory management:
- (id) init
{
self = [super init];
if (self == nil)
return nil;
…
return self;
}
…and then get your instance by calling [[GameStateManager alloc] init].
GameStateManager *manager = [GameStateManager init];
-(id)init is an instance method, not a class method. That line should look like this:
GameStateManager *manager = [[GameStateManager alloc] init];
The issue is that the gameStateManager isn't being created properly. Instead of
[GameStateManager init]
use
[[[GameStateManager alloc] init] autorelease]
The autorelease is for good memory management and doesn't actually affect the initialization.