2010-07-16 15 views
0

J'écris des tests unitaires. Et je ne peux pas tester une fonction, car il appelle keyWindowComment se moquer de la propriété/valeur interne de UIApplication?

UIWindow* window = [UIApplication sharedApplication].keyWindow; 

et keyWindow renvoie nil (je n'ai pas la fenêtre). Mais j'ai besoin de retourner n'importe quoi, mais rien.

I utilisé catégorie pour définir manuellement la valeur keyWindow, mais cela ne fonctionne pas

@interface UIApplication(UnitTest) 
- (id)getKeyWindow; 
@end 

@implementation UIApplication(UnitTest) 
- (id)getKeyWindow 
{ 
    return [self keyWindow]; 
} 
@end 

// compiler error: lvalue required as left operand of assignment 
[[UIApplication sharedApplication] getKeyWindow] = [[UIWindow alloc] init]; 

Que feriez-vous à ma place?

Répondre

3

Apple ne facilite pas le test de votre code lorsque vous commencez à vous rapprocher des classes de structure. J'ai trouvé UIApplication particulièrement difficile en raison de sa nature singleton, et le fait que le framework crée l'instance pour vous lors de l'installation. Les solutions à ces problèmes dépendent généralement de la façon dont vous testez; par exemple, avez-vous un objet UIApplication valide dans vos tests? Si vous utilisez OCUnit alors [UIApplication sharedApplication] peut lui-même renvoyer nil, à moins que vous n'ayez configuré votre cible de test pour qu'elle soit exécutée dans votre bundle d'applications (je n'ai jamais réussi à le faire fonctionner, honnêtement).

Une option consiste simplement à ne pas tester cela. La plupart du temps vos interactions avec la fenêtre principale sont relativement simples, et les tests de ce code testent le framework UIKit autant que votre propre code. C'est une décision stylistique qui dépend de la façon dont vous avez structuré votre code, de la facilité avec laquelle vous laissez cette petite zone non testée (ou, plus opportunément, d'un test automatisé) et de la difficulté d'écrire les tests.

Si vous avez du code qui se rapporte à l'objet UIWindow que vous décidez de tester, je vous suggère d'encapsuler la fonctionnalité d'une manière que vous pouvez ensuite contrôler sous test. Vous pouvez le faire en créant une sous-classe de UIApplication pour votre application qui renvoie l'objet UIWindow à partir d'une méthode personnalisée. Utilisez cette méthode dans votre code, plutôt que d'accéder directement à la propriété window, et dans vos tests, remplacez cette méthode pour renvoyer ce que vous voulez.

0

J'ai été capable de simuler l'objet délégué et de vérifier certaines attentes telles que applicationDidBecomeActive en faisant basculer l'accesseur de délégué d'application.

id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init]; 
UIApplication *app = [UIApplication alloc] init]; 
app.delegate = delegate; 
//results in "There can only be one UIApplication" error 

Au lieu de cela:

@implementation AppDelegateTests { 

- (void)testAppDelegate { 
    //perform swizzle on [UIApplication class] and method @selector(setDelegate:) with 
    //[self class] and at the bottom @selector(swizzledSetDelegate:) 

    id<UIApplicationDelegate> delegate = [MyCustomDelegate alloc] init]; 

    //Here's the magic, this line actually calls [UIApplication setDelegate] and not the swizzledSetDelegate method below. 
    //If you don't understand this, look up how swizzling works and hopefully you can wrap your head around it. The logic is quite mind-bendy. 
    [self swizzledSetDelegate:delegate]; 

    //here set up your mock on the delegate and verify state of things 
} 

- (void)swizzledSetDelegate:(id<UIApplicationDelegate>)delegate { 
    //do nothing 
} 

} 

Notez cet exemple est tout à fait sur mesure pour ce que je devais tester, vous devez penser à ce que vous voulez railler et si son même possible.