1 //---------------------------------------------------------------------------------------
3 // Copyright (c) 2004-2009 by Mulle Kybernetik. See License file for details.
4 //---------------------------------------------------------------------------------------
6 #import <OCMock/OCMockObject.h>
7 #import "OCClassMockObject.h"
8 #import "OCProtocolMockObject.h"
9 #import "OCPartialMockObject.h"
10 #import "OCObserverMockObject.h"
11 #import <OCMock/OCMockRecorder.h>
12 #import "NSInvocation+OCMAdditions.h"
14 @interface OCMockObject(Private)
15 + (id)_makeNice:(OCMockObject *)mock;
16 - (NSString *)_recorderDescriptions:(BOOL)onlyExpectations;
22 @implementation OCMockObject
24 #pragma mark Class initialisation
28 if([[NSInvocation class] instanceMethodSignatureForSelector:@selector(getArgumentAtIndexAsObject:)] == NULL)
29 [NSException raise:NSInternalInconsistencyException format:@"** Expected method not present; the method getArgumentAtIndexAsObject: is not implemented by NSInvocation. If you see this exception it is likely that you are using the static library version of OCMock and your project is not configured correctly to load categories from static libraries. Did you forget to add the -force_load linker flag?"];
33 #pragma mark Factory methods
35 + (id)mockForClass:(Class)aClass
37 return [[[OCClassMockObject alloc] initWithClass:aClass] autorelease];
40 + (id)mockForProtocol:(Protocol *)aProtocol
42 return [[[OCProtocolMockObject alloc] initWithProtocol:aProtocol] autorelease];
45 + (id)partialMockForObject:(NSObject *)anObject
47 return [[[OCPartialMockObject alloc] initWithObject:anObject] autorelease];
51 + (id)niceMockForClass:(Class)aClass
53 return [self _makeNice:[self mockForClass:aClass]];
56 + (id)niceMockForProtocol:(Protocol *)aProtocol
58 return [self _makeNice:[self mockForProtocol:aProtocol]];
62 + (id)_makeNice:(OCMockObject *)mock
71 return [[[OCObserverMockObject alloc] init] autorelease];
76 #pragma mark Initialisers, description, accessors, etc.
80 // no [super init], we're inheriting from NSProxy
81 expectationOrderMatters = NO;
82 recorders = [[NSMutableArray alloc] init];
83 expectations = [[NSMutableArray alloc] init];
84 rejections = [[NSMutableArray alloc] init];
85 exceptions = [[NSMutableArray alloc] init];
92 [expectations release];
98 - (NSString *)description
100 return @"OCMockObject";
104 - (void)setExpectationOrderMatters:(BOOL)flag
106 expectationOrderMatters = flag;
110 #pragma mark Public API
114 OCMockRecorder *recorder = [self getNewRecorder];
115 [recorders addObject:recorder];
122 OCMockRecorder *recorder = [self stub];
123 [expectations addObject:recorder];
130 OCMockRecorder *recorder = [self stub];
131 [rejections addObject:recorder];
138 if([expectations count] == 1)
140 [NSException raise:NSInternalInconsistencyException format:@"%@: expected method was not invoked: %@",
141 [self description], [[expectations objectAtIndex:0] description]];
143 if([expectations count] > 0)
145 [NSException raise:NSInternalInconsistencyException format:@"%@ : %ld expected methods were not invoked: %@",
146 [self description], (unsigned long)[expectations count], [self _recorderDescriptions:YES]];
148 if([exceptions count] > 0)
150 [[exceptions objectAtIndex:0] raise];
156 #pragma mark Handling invocations
158 - (void)forwardInvocation:(NSInvocation *)anInvocation
160 if([self handleInvocation:anInvocation] == NO)
161 [self handleUnRecordedInvocation:anInvocation];
164 - (BOOL)handleInvocation:(NSInvocation *)anInvocation
166 OCMockRecorder *recorder = nil;
169 for(i = 0; i < [recorders count]; i++)
171 recorder = [recorders objectAtIndex:i];
172 if([recorder matchesInvocation:anInvocation])
176 if(i == [recorders count])
179 if([rejections containsObject:recorder])
181 NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:
182 [NSString stringWithFormat:@"%@: explicitly disallowed method invoked: %@", [self description],
183 [anInvocation invocationDescription]] userInfo:nil];
184 [exceptions addObject:exception];
188 if([expectations containsObject:recorder])
190 if(expectationOrderMatters && ([expectations objectAtIndex:0] != recorder))
192 [NSException raise:NSInternalInconsistencyException format:@"%@: unexpected method invoked: %@\n\texpected:\t%@",
193 [self description], [recorder description], [[expectations objectAtIndex:0] description]];
196 [[recorder retain] autorelease];
197 [expectations removeObject:recorder];
198 [recorders removeObjectAtIndex:i];
200 [[recorder invocationHandlers] makeObjectsPerformSelector:@selector(handleInvocation:) withObject:anInvocation];
205 - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation
209 NSException *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:
210 [NSString stringWithFormat:@"%@: unexpected method invoked: %@ %@", [self description],
211 [anInvocation invocationDescription], [self _recorderDescriptions:NO]] userInfo:nil];
212 [exceptions addObject:exception];
218 #pragma mark Helper methods
222 return [[[OCMockRecorder alloc] initWithSignatureResolver:self] autorelease];
226 - (NSString *)_recorderDescriptions:(BOOL)onlyExpectations
228 NSMutableString *outputString = [NSMutableString string];
230 OCMockRecorder *currentObject;
231 NSEnumerator *recorderEnumerator = [recorders objectEnumerator];
232 while((currentObject = [recorderEnumerator nextObject]) != nil)
238 if(![expectations containsObject:currentObject])
244 if ([expectations containsObject:currentObject])
245 prefix = @"expected: ";
247 prefix = @"stubbed: ";
249 [outputString appendFormat:@"\n\t%@\t%@", prefix, [currentObject description]];