Created
March 20, 2014 21:31
-
-
Save kenji21/9674333 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DynamicMethodTest.m | |
// OCMockito | |
// | |
// Created by Richard Bergoin on 20/03/14. | |
// Copyright (c) 2014 Jonathan M. Reid. All rights reserved. | |
// | |
#define MOCKITO_SHORTHAND | |
#import "OCMockito.h" | |
// Test support | |
#import "MockTestCase.h" | |
#import <SenTestingKit/SenTestingKit.h> | |
#define HC_SHORTHAND | |
#if TARGET_OS_MAC | |
#import <OCHamcrest/OCHamcrest.h> | |
#else | |
#import <OCHamcrestIOS/OCHamcrestIOS.h> | |
#endif | |
#include <objc/runtime.h> | |
@interface ObjectUnderTest : NSObject | |
@property (nonatomic, strong) NSMutableDictionary *storage; | |
@property (nonatomic, strong) NSString *name; | |
@end | |
@interface ResolvedMethodUnderTest : ObjectUnderTest | |
@end | |
@interface ForwardMethodUnderTest : ObjectUnderTest | |
@end | |
@interface DynamicMethodTest : SenTestCase | |
@end | |
@implementation ObjectUnderTest | |
@dynamic name; | |
- (instancetype)init | |
{ | |
self = [super init]; | |
if (self) { | |
_storage = [[NSMutableDictionary alloc] init]; | |
} | |
return self; | |
} | |
@end | |
static NSString *AttributeNameFromSetSelectorString(NSString *setSelectorString) | |
{ | |
NSMutableString *attrName = [setSelectorString mutableCopy]; | |
NSString *firstLowercase = [[setSelectorString substringWithRange:NSMakeRange(3, 1)] lowercaseString]; | |
NSInteger objectLen = [@":" length]; | |
[attrName replaceCharactersInRange:NSMakeRange(0, 4) withString:firstLowercase]; // setN -> n | |
[attrName deleteCharactersInRange:NSMakeRange([attrName length] - objectLen, objectLen)]; | |
return attrName; | |
} | |
@implementation ResolvedMethodUnderTest | |
static id dynamicObjectGetterMethod(id self, SEL _cmd) | |
{ | |
ObjectUnderTest *object = self; | |
NSString *attrName = NSStringFromSelector(_cmd); | |
return [object.storage objectForKey:attrName]; | |
} | |
static void dynamicObjectSetterMethod(id self, SEL _cmd, id value) | |
{ | |
ObjectUnderTest *object = self; | |
NSString *setterName = NSStringFromSelector(_cmd); // setFirstName: | |
NSString *attrName = AttributeNameFromSetSelectorString(setterName); | |
if( value ) | |
{ | |
[object.storage setObject:value forKey:attrName]; | |
} | |
else if( value == nil ) | |
{ | |
[object.storage removeObjectForKey:attrName]; | |
} | |
} | |
+ (BOOL)resolveInstanceMethod:(SEL)sel | |
{ | |
NSString *selStr = NSStringFromSelector(sel); | |
if( [selStr hasPrefix:@"set"] && [selStr hasSuffix:@":"] ) | |
{ | |
const char *types = "v@:@"; // first is return type (see @encode), second is self type, third is _cmd, fourth is param type | |
return class_addMethod(self, sel, (IMP)dynamicObjectSetterMethod, types); | |
} | |
else if( [selStr rangeOfString:@":"].location == NSNotFound ) | |
{ | |
const char *types = "@@:"; | |
return class_addMethod(self, sel, (IMP)dynamicObjectGetterMethod, types); | |
} | |
return [super resolveInstanceMethod:sel]; | |
} | |
@end | |
@implementation ForwardMethodUnderTest | |
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector | |
{ | |
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector]; | |
if( signature == nil ) | |
{ | |
signature = [[self class] instanceMethodSignatureForSelector:aSelector]; | |
} | |
return signature; | |
} | |
+ (NSMethodSignature *)instanceMethodSignatureForDynamicPropertyWithSelector:(SEL)aSelector | |
{ | |
NSMethodSignature *signature = nil; | |
NSString *sel = NSStringFromSelector(aSelector); | |
if ([sel rangeOfString:@"set"].location == 0) | |
{ | |
signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"]; | |
} | |
else | |
{ | |
signature = [NSMethodSignature signatureWithObjCTypes:"@@:"]; | |
} | |
return signature; | |
} | |
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector | |
{ | |
NSMethodSignature *signature = [super instanceMethodSignatureForSelector:aSelector]; | |
if( signature == nil ) | |
{ | |
signature = [self instanceMethodSignatureForDynamicPropertyWithSelector:aSelector]; | |
} | |
return signature; | |
} | |
- (void)forwardInvocation:(NSInvocation *)invocation | |
{ | |
NSString *selStr = NSStringFromSelector([invocation selector]); | |
if ([selStr rangeOfString:@"set"].location == 0) | |
{ | |
NSString *attrName = AttributeNameFromSetSelectorString(selStr); | |
NSString *obj; | |
[invocation getArgument:&obj atIndex:2]; | |
[self.storage setObject:obj forKey:attrName]; | |
} | |
else | |
{ | |
NSString *attrName = selStr; | |
NSString *obj = [self.storage objectForKey:attrName]; | |
[invocation setReturnValue:&obj]; | |
} | |
} | |
@end | |
@implementation DynamicMethodTest | |
- (void)testNumberOfArgumentsOnResolvedGetter | |
{ | |
ResolvedMethodUnderTest *ut = [[ResolvedMethodUnderTest alloc] init]; | |
NSMethodSignature *getterSignature = [ut methodSignatureForSelector:@selector(name)]; | |
NSInteger num = [getterSignature numberOfArguments]; | |
STAssertNotNil(getterSignature, @""); | |
STAssertEquals(num, (NSInteger)2, @""); | |
} | |
- (void)testNumberOfArgumentsOnForwardGetter | |
{ | |
ForwardMethodUnderTest *ut = [[ForwardMethodUnderTest alloc] init]; | |
NSMethodSignature *getterSignature = [ut methodSignatureForSelector:@selector(name)]; | |
NSInteger num = [getterSignature numberOfArguments]; | |
STAssertNotNil(getterSignature, @""); | |
STAssertEquals(num, (NSInteger)2, @""); | |
} | |
- (void)testNumberOfArgumentsOnResolvedSetter | |
{ | |
ResolvedMethodUnderTest *ut = [[ResolvedMethodUnderTest alloc] init]; | |
NSMethodSignature *setterSignature = [ut methodSignatureForSelector:@selector(setName:)]; | |
NSInteger num = [setterSignature numberOfArguments]; | |
STAssertNotNil(setterSignature, @""); | |
STAssertEquals(num, (NSInteger)3, @""); | |
} | |
- (void)testNumberOfArgumentsOnForwardSetter | |
{ | |
ForwardMethodUnderTest *ut = [[ForwardMethodUnderTest alloc] init]; | |
NSMethodSignature *setterSignature = [ut methodSignatureForSelector:@selector(setName:)]; | |
NSInteger num = [setterSignature numberOfArguments]; | |
STAssertNotNil(setterSignature, @""); | |
STAssertEquals(num, (NSInteger)3, @""); | |
} | |
- (void)testSetFirstNameOnResolved | |
{ | |
ResolvedMethodUnderTest *ut = [[ResolvedMethodUnderTest alloc] init]; | |
[ut setName:@"Clément"]; | |
STAssertEqualObjects(ut.name, @"Clément", @""); | |
} | |
- (void)testSetFirstNameOnResolvedMock | |
{ | |
ResolvedMethodUnderTest *ut = mock([ResolvedMethodUnderTest class]); | |
[ut setName:@"Clément"]; | |
[verify(ut) setName:@"Clément"]; | |
} | |
- (void)testSetFirstNameOnForward | |
{ | |
ForwardMethodUnderTest *ut = [[ForwardMethodUnderTest alloc] init]; | |
[ut setName:@"Clément"]; | |
STAssertEqualObjects(ut.name, @"Clément", @""); | |
} | |
- (void)testSetFirstNameOnForwardMock | |
{ | |
ForwardMethodUnderTest *ut = mock([ForwardMethodUnderTest class]); | |
[ut setName:@"Clément"]; | |
[verify(ut) setName:@"Clément"]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment