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 "WOObjectStub.h"
30 @implementation WOObjectMock
35 + (id)mockForClass:(Class)aClass
37 NSParameterAssert(aClass != NULL);
38 NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]); // only registered classes pass (do not pass meta classes)
39 return [[self alloc] initWithClass:aClass];
42 - (id)initWithClass:(Class)aClass
44 NSParameterAssert(aClass != NULL);
45 NSParameterAssert([NSObject WOTest_isRegisteredClass:aClass]); // only registered classes pass (do not pass meta classes)
47 if ((self = [super init]))
48 [self setMockedClass:aClass];
53 #pragma mark Recording
57 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
58 [accepted addObject:stub];
64 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
65 [acceptedOnce addObject:stub];
71 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
72 [rejected addObject:stub];
78 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
79 [expected addObject:stub];
85 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
86 [expectedOnce addObject:stub];
92 WOObjectStub *stub = [WOObjectStub stubForClass:[self mockedClass] withDelegate:self];
93 [expectedInOrder addObject:stub];
98 #pragma mark Proxy methods
100 - (void)forwardInvocation:(NSInvocation *)anInvocation
102 NSParameterAssert(anInvocation != nil);
104 // check if in reject list
105 for (WOStub *stub in rejected)
106 if ([stub matchesInvocation:anInvocation])
108 [NSException raise:NSInternalInconsistencyException format:@"Rejected selector %@ for class %@",
109 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
113 // check if expectedInOrder
114 for (WOStub *stub in expectedInOrder)
115 if ([stub matchesInvocation:anInvocation])
117 NSAssert2(([expectedInOrder objectAtIndex:0] == stub), @"Invocation selector %@ class %@ received out of order",
118 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass]));
120 [expectedInOrder removeObjectAtIndex:0]; // if in order, remove from head of list
122 // and move to "accepted"
123 [accepted addObject:stub];
124 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
125 if ([stub exception]) @throw [stub exception];
129 // check if expected once
130 for (WOStub *stub in expectedOnce)
131 if ([stub matchesInvocation:anInvocation])
133 // move object from "expectedOnce" to "rejected"
134 [rejected addObject:stub];
135 [expectedOnce removeObject:stub];
136 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
137 if ([stub exception]) @throw [stub exception];
142 for (WOStub *stub in expected)
143 if ([stub matchesInvocation:anInvocation])
145 // move from "expected" to "accepted"
146 [accepted addObject:stub];
147 [expected removeObject:stub];
148 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
149 if ([stub exception]) @throw [stub exception];
153 // check if accepted once
154 for (WOStub *stub in acceptedOnce)
155 if ([stub matchesInvocation:anInvocation])
157 // move from "acceptedOnce" to "rejected"
158 [rejected addObject:stub];
159 [acceptedOnce removeObject:stub];
160 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
161 if ([stub exception]) @throw [stub exception];
166 for (WOStub *stub in accepted)
167 if ([stub matchesInvocation:anInvocation])
169 [self storeReturnValue:[stub returnValue] forInvocation:anInvocation];
170 if ([stub exception]) @throw [stub exception];
174 if ([self acceptsByDefault]) return;
176 // no matches! (should never get here)
177 [NSException raise:NSInternalInconsistencyException format:@"No matching invocations found (selector %@, class %@)",
178 NSStringFromSelector([anInvocation selector]), NSStringFromClass([self mockedClass])];
181 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
183 // avoid an infinite loop (docs warn "Be sure to avoid an infinite loop when necessary by checking that aSelector isn't the
184 // selector for this method itself and by not sending any message that might invoke this method.")
185 if (([self mockedClass] == [self class]) && (aSelector == _cmd)) return nil;
187 // search only for instance methods here; forcing the programmer to use WOClassMock for searching for class methods avoids
188 // ambiguity in cases where an instance method and a class method share the same name
189 return [[self mockedClass] instanceMethodSignatureForSelector:aSelector];
193 #pragma mark Accessors
200 - (void)setMockedClass:(Class)aMockedClass
202 mockedClass = aMockedClass;