5 // Created by Wincent Colaiuta on 10 February 2006.
7 // Copyright 2006-2007 Wincent Colaiuta.
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation, either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #import "WOObjectMock.h"
26 #import "NSInvocation+WOTest.h"
27 #import "NSObject+WOTest.h"
28 #import "WOEnumerate.h"
29 #import "WOObjectStub.h"
31 @implementation WOObjectMock
36 + (id)mockForClass:(Class)aClass
38 NSParameterAssert(aClass != NULL);
39 NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]); // only registered classes pass (do not pass meta classes)
40 return [[[self alloc] initWithClass:aClass] autorelease];
43 - (id)initWithClass:(Class)aClass
45 NSParameterAssert(aClass != NULL);
46 NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]); // only registered classes pass (do not pass meta classes)
48 if ((self = [super init]))
49 [self setMockedClass:aClass];
54 #pragma mark Recording
58 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
59 [accepted addObject:stub];
65 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
66 [acceptedOnce addObject:stub];
72 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
73 [rejected addObject:stub];
79 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
80 [expected addObject:stub];
86 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
87 [expectedOnce addObject:stub];
93 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
94 [expectedInOrder addObject:stub];
99 #pragma mark Proxy methods
101 - (void)forwardInvocation:(NSInvocation *)anInvocation
103 NSParameterAssert(anInvocation != nil);
105 // check if in reject list
106 WO_ENUMERATE(rejected, stub)
107 if ([stub matchesInvocation:anInvocation])
109 [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for class %@",
110 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
114 // check if expectedInOrder
115 WO_ENUMERATE(expectedInOrder, stub)
116 if ([stub matchesInvocation:anInvocation])
118 NSAssert2(([expectedInOrder objectAtIndex:0] == stub), @"Invocation selector %@ class %@ received out of order",
119 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass]));
121 [expectedInOrder removeObjectAtIndex:0]; // if in order, remove from head of list
123 // and move to "accepted"
124 [accepted addObject:stub];
125 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
126 if ([stub exception]) @throw [stub exception];
130 // check if expected once
131 WO_ENUMERATE(expectedOnce, stub)
132 if ([stub matchesInvocation:anInvocation])
134 // move object from "expectedOnce" to "rejected"
135 [rejected addObject:stub];
136 [expectedOnce removeObject:stub];
137 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
138 if ([stub exception]) @throw [stub exception];
143 WO_ENUMERATE(expected, stub)
144 if ([stub matchesInvocation:anInvocation])
146 // move from "expected" to "accepted"
147 [accepted addObject:stub];
148 [expected removeObject:stub];
149 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
150 if ([stub exception]) @throw [stub exception];
154 // check if accepted once
155 WO_ENUMERATE(acceptedOnce, stub)
156 if ([stub matchesInvocation:anInvocation])
158 // move from "acceptedOnce" to "rejected"
159 [rejected addObject:stub];
160 [acceptedOnce removeObject:stub];
161 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
162 if ([stub exception]) @throw [stub exception];
167 WO_ENUMERATE(accepted, stub)
168 if ([stub matchesInvocation:anInvocation])
170 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
171 if ([stub exception]) @throw [stub exception];
175 if ([self acceptsByDefault]) return;
177 // no matches! (should never get here)
178 [NSException raise:NSInternalInconsistencyException format:@"No matching invocations found (selector %@, class %@)",
179 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
182 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
184 // avoid an infinite loop (docs warn "Be sure to avoid an infinite loop when necessary by checking that aSelector isn't the
185 // selector for this method itself and by not sending any message that might invoke this method.")
186 if (([self mockedClass] == [self class]) && (aSelector == _cmd)) return nil;
188 // search only for instance methods here; forcing the programmer to use WOClassMock for searching for class methods avoids
189 // ambiguity in cases where an instance method and a class method share the same name
190 return [[self mockedClass] instanceMethodSignatureForSelector:aSelector];
194 #pragma mark Accessors
201 - (void)setMockedClass:(Class)aMockedClass
203 mockedClass = aMockedClass;