NSObject+KiwiStubAdditions.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. //
  2. // Licensed under the terms in License.txt
  3. //
  4. // Copyright 2010 Allen Ding. All rights reserved.
  5. //
  6. #import "NSObject+KiwiStubAdditions.h"
  7. #import "KWCaptureSpy.h"
  8. #import "KWIntercept.h"
  9. #import "KWInvocationCapturer.h"
  10. #import "KWMessagePattern.h"
  11. #import "KWObjCUtilities.h"
  12. #import "KWStringUtilities.h"
  13. #import "KWStub.h"
  14. static NSString * const StubValueKey = @"StubValueKey";
  15. static NSString * const StubSecondValueKey = @"StubSecondValueKey";
  16. static NSString * const ChangeStubValueAfterTimesKey = @"ChangeStubValueAfterTimesKey";
  17. @implementation NSObject(KiwiStubAdditions)
  18. #pragma mark - Capturing Invocations
  19. - (NSMethodSignature *)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer methodSignatureForSelector:(SEL)aSelector {
  20. NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
  21. if (signature != nil)
  22. return signature;
  23. NSString *encoding = KWEncodingForDefaultMethod();
  24. return [NSMethodSignature signatureWithObjCTypes:[encoding UTF8String]];
  25. }
  26. - (void)invocationCapturer:(KWInvocationCapturer *)anInvocationCapturer didCaptureInvocation:(NSInvocation *)anInvocation {
  27. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternFromInvocation:anInvocation];
  28. id value = (anInvocationCapturer.userInfo)[StubValueKey];
  29. if (!(anInvocationCapturer.userInfo)[StubSecondValueKey]) {
  30. [self stubMessagePattern:messagePattern andReturn:value];
  31. } else {
  32. id times = (anInvocationCapturer.userInfo)[ChangeStubValueAfterTimesKey];
  33. id secondValue = (anInvocationCapturer.userInfo)[StubSecondValueKey];
  34. [self stubMessagePattern:messagePattern andReturn:value times:times afterThatReturn:secondValue];
  35. }
  36. }
  37. #pragma mark - Stubbing Methods
  38. - (void)stub:(SEL)aSelector {
  39. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  40. [self stubMessagePattern:messagePattern andReturn:nil];
  41. }
  42. - (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block {
  43. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  44. [self stubMessagePattern:messagePattern withBlock:block];
  45. }
  46. - (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
  47. va_list argumentList;
  48. va_start(argumentList, firstArgument);
  49. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  50. [self stubMessagePattern:messagePattern andReturn:nil];
  51. }
  52. - (void)stub:(SEL)aSelector andReturn:(id)aValue {
  53. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  54. [self stubMessagePattern:messagePattern andReturn:aValue];
  55. }
  56. - (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
  57. va_list argumentList;
  58. va_start(argumentList, firstArgument);
  59. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  60. [self stubMessagePattern:messagePattern andReturn:aValue];
  61. }
  62. - (void)stub:(SEL)aSelector andReturn:(id)aValue times:(NSNumber *)times afterThatReturn:(id)aSecondValue {
  63. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  64. [self stubMessagePattern:messagePattern andReturn:aValue times:times afterThatReturn:aSecondValue];
  65. }
  66. + (void)stub:(SEL)aSelector {
  67. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  68. [self stubMessagePattern:messagePattern andReturn:nil];
  69. }
  70. + (void)stub:(SEL)aSelector withBlock:(id (^)(NSArray *))block {
  71. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  72. [self stubMessagePattern:messagePattern withBlock:block];
  73. }
  74. + (void)stub:(SEL)aSelector withArguments:(id)firstArgument, ... {
  75. va_list argumentList;
  76. va_start(argumentList, firstArgument);
  77. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  78. [self stubMessagePattern:messagePattern andReturn:nil];
  79. }
  80. + (void)stub:(SEL)aSelector andReturn:(id)aValue {
  81. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  82. [self stubMessagePattern:messagePattern andReturn:aValue];
  83. }
  84. + (void)stub:(SEL)aSelector andReturn:(id)aValue withArguments:(id)firstArgument, ... {
  85. va_list argumentList;
  86. va_start(argumentList, firstArgument);
  87. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector firstArgumentFilter:firstArgument argumentList:argumentList];
  88. [self stubMessagePattern:messagePattern andReturn:aValue];
  89. }
  90. + (void)stub:(SEL)aSelector andReturn:(id)aValue times:(NSNumber *)times afterThatReturn:(id)aSecondValue {
  91. KWMessagePattern *messagePattern = [KWMessagePattern messagePatternWithSelector:aSelector];
  92. [self stubMessagePattern:messagePattern andReturn:aValue times:times afterThatReturn:aSecondValue];
  93. }
  94. - (id)stub {
  95. return [KWInvocationCapturer invocationCapturerWithDelegate:self];
  96. }
  97. - (id)stubAndReturn:(id)aValue {
  98. NSDictionary *userInfo = @{StubValueKey: aValue};
  99. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  100. }
  101. - (id)stubAndReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
  102. NSDictionary *userInfo = @{StubValueKey: aValue, ChangeStubValueAfterTimesKey: times, StubSecondValueKey: aSecondValue};
  103. return [KWInvocationCapturer invocationCapturerWithDelegate:self userInfo:userInfo];
  104. }
  105. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue {
  106. [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
  107. }
  108. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)overrideExisting {
  109. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  110. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  111. NSStringFromSelector(aMessagePattern.selector)];
  112. }
  113. Class interceptClass = KWSetupObjectInterceptSupport(self);
  114. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  115. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
  116. KWAssociateObjectStub(self, stub, overrideExisting);
  117. }
  118. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
  119. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  120. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  121. NSStringFromSelector(aMessagePattern.selector)];
  122. }
  123. Class interceptClass = KWSetupObjectInterceptSupport(self);
  124. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  125. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
  126. KWAssociateObjectStub(self, stub, YES);
  127. }
  128. - (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
  129. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  130. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  131. NSStringFromSelector(aMessagePattern.selector)];
  132. }
  133. Class interceptClass = KWSetupObjectInterceptSupport(self);
  134. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  135. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
  136. KWAssociateObjectStub(self, stub, YES);
  137. }
  138. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue {
  139. [self stubMessagePattern:aMessagePattern andReturn:aValue overrideExisting:YES];
  140. }
  141. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue overrideExisting:(BOOL)override {
  142. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  143. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  144. NSStringFromSelector(aMessagePattern.selector)];
  145. }
  146. Class interceptClass = KWSetupObjectInterceptSupport(self);
  147. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  148. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue];
  149. KWAssociateObjectStub(self, stub, override);
  150. }
  151. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue {
  152. [self stubMessagePattern:aMessagePattern andReturn:aValue times:times afterThatReturn:aSecondValue overrideExisting:YES];
  153. }
  154. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern andReturn:(id)aValue times:(id)times afterThatReturn:(id)aSecondValue overrideExisting:(BOOL)override {
  155. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  156. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  157. NSStringFromSelector(aMessagePattern.selector)];
  158. }
  159. Class interceptClass = KWSetupObjectInterceptSupport(self);
  160. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  161. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern value:aValue times:times afterThatReturn:aSecondValue];
  162. KWAssociateObjectStub(self, stub, override);
  163. }
  164. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block {
  165. [self stubMessagePattern:aMessagePattern withBlock:block overrideExisting:YES];
  166. }
  167. + (void)stubMessagePattern:(KWMessagePattern *)aMessagePattern withBlock:(id (^)(NSArray *params))block overrideExisting:(BOOL)override {
  168. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  169. [NSException raise:@"KWStubException" format:@"cannot stub -%@ because no such method exists",
  170. NSStringFromSelector(aMessagePattern.selector)];
  171. }
  172. Class interceptClass = KWSetupObjectInterceptSupport(self);
  173. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  174. KWStub *stub = [KWStub stubWithMessagePattern:aMessagePattern block:block];
  175. KWAssociateObjectStub(self, stub, override);
  176. }
  177. - (void)clearStubs {
  178. KWClearObjectStubs(self);
  179. }
  180. #pragma mark - Spying on Messages
  181. - (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  182. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  183. [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists",
  184. NSStringFromSelector(aMessagePattern.selector)];
  185. }
  186. Class interceptClass = KWSetupObjectInterceptSupport(self);
  187. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  188. KWAssociateMessageSpy(self, aSpy, aMessagePattern);
  189. }
  190. - (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  191. KWClearObjectSpy(self, aSpy, aMessagePattern);
  192. }
  193. - (KWCaptureSpy *)captureArgument:(SEL)selector atIndex:(NSUInteger)index {
  194. KWCaptureSpy *spy = [[KWCaptureSpy alloc] initWithArgumentIndex:index];
  195. [self addMessageSpy:spy forMessagePattern:[KWMessagePattern messagePatternWithSelector:selector]];
  196. return spy;
  197. }
  198. + (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  199. if ([self methodSignatureForSelector:aMessagePattern.selector] == nil) {
  200. [NSException raise:@"KWSpyException" format:@"cannot add spy for -%@ because no such method exists",
  201. NSStringFromSelector(aMessagePattern.selector)];
  202. }
  203. Class interceptClass = KWSetupObjectInterceptSupport(self);
  204. KWSetupMethodInterceptSupport(interceptClass, aMessagePattern.selector);
  205. KWAssociateMessageSpy(self, aSpy, aMessagePattern);
  206. }
  207. + (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
  208. KWClearObjectSpy(self, aSpy, aMessagePattern);
  209. }
  210. + (KWCaptureSpy *)captureArgument:(SEL)selector atIndex:(NSUInteger)index {
  211. KWCaptureSpy *spy = [[KWCaptureSpy alloc] initWithArgumentIndex:index];
  212. [self addMessageSpy:spy forMessagePattern:[KWMessagePattern messagePatternWithSelector:selector]];
  213. return spy;
  214. }
  215. @end