5 // Created by Wincent Colaiuta on 29 January 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/>.
22 #import "WOObjectMockTests.h"
24 @implementation WOObjectMockTests
27 #pragma mark High-level tests
29 - (void)testMockForClass
31 // should throw if passed NULL
32 WO_TEST_THROWS([WOObjectMock mockForClass:NULL]);
34 // should throw if passed non-class pointer
35 WO_TEST_THROWS([WOObjectMock mockForClass:(Class)self]);
37 // otherwise should work
38 WO_TEST_DOES_NOT_THROW([WOObjectMock mockForClass:[self class]]);
40 // should throw if passed a meta class
41 Class class = [NSString class];
42 Class metaclass = object_getClass(class);
43 WO_TEST_THROWS([WOObjectMock mockForClass:metaclass]);
46 - (void)testInitWithClass
48 // should throw if passed NULL
49 WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:NULL]);
51 // should throw if passed non-class pointer
52 WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:(Class)self]);
54 // otherwise should work
55 WO_TEST_DOES_NOT_THROW([[WOObjectMock alloc] initWithClass:[self class]]);
57 // should throw if passed a meta class
58 WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:object_getClass([NSString class])]);
61 - (void)testMockExpectInOrder
64 id mock = [WOObjectMock mockForClass:[NSString class]];
66 [[mock expectInOrder] lowercaseString];
67 [[mock expectInOrder] uppercaseString];
68 [[mock expectInOrder] stringByExpandingTildeInPath];
69 [[mock expectInOrder] uppercaseString];
71 [mock lowercaseString];
72 [mock uppercaseString];
73 [mock stringByExpandingTildeInPath];
74 [mock uppercaseString];
76 WO_TEST_DOES_NOT_THROW([mock verify]);
78 // repeat test: this time omit one of the expected methods
80 [[mock expectInOrder] lowercaseString];
81 [[mock expectInOrder] uppercaseString];
82 [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
83 [[mock expectInOrder] uppercaseString];
85 [mock lowercaseString];
86 [mock uppercaseString];
87 [mock stringByAbbreviatingWithTildeInPath];
89 WO_TEST_THROWS([mock verify]);
91 // repeat test: this time invoke methods in wrong order
93 [[mock expectInOrder] lowercaseString];
94 [[mock expectInOrder] uppercaseString];
95 [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
96 [[mock expectInOrder] uppercaseString];
98 [mock lowercaseString];
99 WO_TEST_THROWS([mock stringByAbbreviatingWithTildeInPath]);
101 // test with arguments
103 [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
104 [[mock expectInOrder] uppercaseString];
105 [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
106 [[mock expectInOrder] uppercaseString];
108 [mock stringByAppendingFormat:@"foobar"];
109 [mock uppercaseString];
110 [mock stringByAbbreviatingWithTildeInPath];
111 [mock uppercaseString];
113 WO_TEST_DOES_NOT_THROW([mock verify]);
115 // repeat test: this time pass unexpected argument
117 [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
118 [[mock expectInOrder] uppercaseString];
119 [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
120 [[mock expectInOrder] uppercaseString];
122 WO_TEST_THROWS([mock stringByAppendingFormat:@"other"]);
124 // test with object return value
125 NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
127 [[[mock expectInOrder] returning:value] stringByAppendingString:@"bar"];
128 WO_TEST_EQ(@"foobar", [mock stringByAppendingString:@"bar"]);
129 WO_TEST_DOES_NOT_THROW([mock verify]);
131 // test with scalar return value
132 value = [NSValue WOTest_valueWithUnsignedInt:6];
134 [[[mock expectInOrder] returning:value] length];
135 WO_TEST_EQ((unsigned int)6, [mock length]);
136 WO_TEST_DOES_NOT_THROW([mock verify]);
138 // test with raising exception
140 [[[mock expectInOrder] raising:self] lowercaseString];
141 WO_TEST_THROWS([mock lowercaseString]);
143 // test raising named exception
144 NSException *exception = [NSException exceptionWithName:@"Robert"
145 reason:@"Robert exception"
148 [[[mock expectInOrder] raising:exception] lowercaseString];
149 WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
152 - (void)testMockExpectOnce
155 id mock = [WOObjectMock mockForClass:[NSString class]];
156 [[mock expectOnce] lowercaseString];
157 WO_TEST_THROWS([mock verify]); // was bug
158 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
159 WO_TEST_THROWS([mock lowercaseString]);
160 WO_TEST_DOES_NOT_THROW([mock verify]);
162 // test with arguments
164 [[mock expectOnce] stringByAppendingFormat:@"foo"];
165 WO_TEST_THROWS([mock verify]);
166 WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
167 WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
168 WO_TEST_DOES_NOT_THROW([mock verify]);
170 // repeat test: this time pass unexpected argument
172 [[mock expectOnce] stringByAppendingFormat:@"foo"];
173 WO_TEST_THROWS([mock verify]);
174 WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
175 WO_TEST_THROWS([mock verify]);
177 // test with return value
178 NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
180 [[[mock expectOnce] returning:value] stringByAppendingString:@"bar"];
181 WO_TEST_THROWS([mock verify]);
182 WO_TEST_EQ(@"foobar", [mock stringByAppendingString:@"bar"]);
183 WO_TEST_DOES_NOT_THROW([mock verify]);
185 // test with raising exception
187 [[[mock expectOnce] raising:self] lowercaseString];
188 WO_TEST_THROWS([mock verify]);
189 WO_TEST_THROWS([mock lowercaseString]);
190 WO_TEST_DOES_NOT_THROW([mock verify]);
192 // test raising named exception
193 NSException *exception = [NSException exceptionWithName:@"Robert"
194 reason:@"Robert exception"
197 [[[mock expectOnce] raising:exception] lowercaseString];
198 WO_TEST_THROWS([mock verify]);
199 WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
200 WO_TEST_DOES_NOT_THROW([mock verify]);
203 - (void)testMockExpect
206 id mock = [WOObjectMock mockForClass:[NSString class]];
207 [[mock expect] lowercaseString];
208 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
209 WO_TEST_DOES_NOT_THROW([mock verify]);
210 WO_TEST_THROWS([mock uppercaseString]);
212 // test with return value (no parameters)
213 NSValue *value = [NSValue WOTest_valueWithObject:@".txt"];
215 [[[mock expect] returning:value] stringByAppendingPathExtension:@"txt"];
216 WO_TEST_EQ([mock stringByAppendingPathExtension:@"txt"], @".txt");
217 WO_TEST_DOES_NOT_THROW([mock verify]);
219 // test with parameters and return value
221 value = [NSValue WOTest_valueWithObject:@"foo.txt"];
222 [[[mock expect] returning:value] stringByAppendingFormat:@".txt"];
223 WO_TEST_EQ([mock stringByAppendingFormat:@".txt"], @"foo.txt");
224 WO_TEST_THROWS([mock stringByAppendingFormat:@".m"]); // wrong argument
226 // test with parameters but without return value
228 [[mock expect] stringByAppendingFormat:@".mov"];
229 WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@".mov"]);
230 WO_TEST_THROWS([mock stringByAppendingFormat:@".mpeg"]);
231 WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@".mov"]);
234 - (void)testMockAcceptOnce
237 id mock = [WOObjectMock mockForClass:[NSString class]];
238 [[mock acceptOnce] lowercaseString];
239 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
240 WO_TEST_THROWS([mock lowercaseString]);
242 // test with arguments
244 [[mock acceptOnce] stringByAppendingFormat:@"foo"];
245 WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
246 WO_TEST_THROWS([mock stringByAppendingFormat:@"foo"]);
248 // repeat test: this time pass unexpected argument
250 [[mock acceptOnce] stringByAppendingFormat:@"foo"];
251 WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
252 WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
253 WO_TEST_THROWS([mock stringByAppendingFormat:@"foo"]);
255 // test with return value
256 NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
258 [[[mock acceptOnce] returning:value] stringByAppendingFormat:@"bar"];
259 WO_TEST_EQ([mock stringByAppendingFormat:@"bar"], @"foobar");
260 WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
262 // test raising exception
264 [[[mock acceptOnce] raising:@"foo"] lowercaseString];
265 WO_TEST_THROWS([mock lowercaseString]);
267 // test raising named exception
268 NSException *exception = [NSException exceptionWithName:@"Robert"
269 reason:@"Robert exception"
272 [[[mock acceptOnce] raising:exception] lowercaseString];
273 WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
276 - (void)testMockAccept
281 // should throw (NSString instances do not respond to -stringWithString)
282 // they do respond to the +stringWithString class method
283 mock = [WOObjectMock mockForClass:[NSString class]];
284 WO_TEST_THROWS([[mock accept] stringWithString:@"Hello"]);
285 WO_TEST_THROWS([mock stringWithString:@"Hello"]);
287 // should throw even for valid selectors if you haven't set them up first
288 mock = [WOObjectMock mockForClass:[NSString class]];
289 [[mock expect] lowercaseString]; // a valid NSString selector
290 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
291 WO_TEST_DOES_NOT_THROW([mock verify]);
292 WO_TEST_THROWS([mock uppercaseString]); // fail (not explicitly expected)
294 // should throw for class methods
295 mock = [WOObjectMock mockForClass:[NSString class]];
296 WO_TEST_THROWS([[mock expect] stringWithString:@"foo"]);
297 WO_TEST_THROWS([mock stringWithString:@"foo"]);
302 // test that clear actually clears
303 id mock = [WOObjectMock mockForClass:[NSString class]];
304 [[mock accept] lowercaseString];
305 WO_TEST_DOES_NOT_THROW([mock lowercaseString]); // send as many times as
306 WO_TEST_DOES_NOT_THROW([mock lowercaseString]); // you like... should work
308 WO_TEST_THROWS([mock lowercaseString]); // but fail on clear
310 // should be able to clear and re-set the same selector (was a bug)
311 [[mock accept] lowercaseString];
312 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
314 [[mock accept] lowercaseString];
315 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
318 // same for acceptOnce
319 [[mock acceptOnce] lowercaseString];
321 WO_TEST_THROWS([mock lowercaseString]);
324 [[mock expect] lowercaseString];
326 WO_TEST_THROWS([mock lowercaseString]);
329 [[mock expectOnce] lowercaseString];
331 WO_TEST_THROWS([mock lowercaseString]);
334 [[mock expectInOrder] lowercaseString];
336 WO_TEST_THROWS([mock lowercaseString]);
339 - (void)testAnyArguments
341 // by default should require exactly matching arguments
342 id mock = [WOObjectMock mockForClass:[NSString class]];
343 [[mock accept] stringByAppendingString:@"foo"];
344 WO_TEST_THROWS([mock stringByAppendingString:@"bar"]);
345 WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"foo"]);
346 WO_TEST_THROWS([mock stringByAppendingString:nil]);
348 // but should also be able to accept any argument
350 [[[mock accept] anyArguments] stringByAppendingString:@"irrelevant"];
351 WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"irrelevant"]);
352 WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"bar"]);
353 WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:@"foo"]);
354 WO_TEST_DOES_NOT_THROW([mock stringByAppendingString:nil]);
357 - (void)testAcceptsByDefault
359 id mock = [WOObjectMock mockForClass:[NSString class]];
360 [mock setAcceptsByDefault:YES];
361 WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
363 // unrecognised selector should always throw
364 WO_TEST_THROWS(objc_msgSend(mock, @selector(foobar)));
366 [mock setAcceptsByDefault:NO];
367 WO_TEST_THROWS([mock lowercaseString]);
370 - (void)testReturning
372 // should work for scalars too
373 id mock = [WOObjectMock mockForClass:[NSString class]];
374 unsigned int length = 20;
375 NSValue *value = [NSValue value:&length withObjCType:@encode(unsigned int)];
376 [[[mock accept] returning:value] length];
377 WO_TEST_EQ([mock length], length);
378 WO_TEST_EQ([mock length], (unsigned int)20);
382 #pragma mark Low-level tests
387 Class aClass = [self class];
389 // should raise if passed nil class
390 WO_TEST_THROWS([WOObjectMock mockForClass:nil]);
391 WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:nil]);
392 WO_TEST_DOES_NOT_THROW([WOObjectMock mockForClass:aClass]);
393 WO_TEST_DOES_NOT_THROW([[WOObjectMock alloc] initWithClass:aClass]);
395 // test if passed a non-class object (ie. an instance)
400 - (void)testObjectStub
404 Class aClass = [self class];
406 // raise if initialized with nil class pointer
407 WO_TEST_DOES_NOT_THROW([WOObjectStub stubForClass:aClass withDelegate:nil]);
408 WO_TEST_THROWS([WOObjectStub stubForClass:nil withDelegate:nil]);
409 WO_TEST_DOES_NOT_THROW([[WOObjectStub alloc] initWithClass:aClass delegate:nil]);
410 WO_TEST_THROWS([[WOObjectStub alloc] initWithClass:nil delegate:nil]);
412 // test if passed a non-class object (ie. an instance)
416 // shouldn't crash even if passed non-NSObject descendant
421 // make sure methodSignatureForSelector: doesn't go into an infinite loop
422 stub = [WOObjectStub stubForClass:[WOObjectStub class] withDelegate:nil];
423 SEL selector = @selector(methodSignatureForSelector:);
424 WO_TEST_EQUAL([stub methodSignatureForSelector:selector], nil);
426 // not with subclasses either
429 // raise if returning: is invoked twice
430 stub = [WOObjectStub stubForClass:[NSString class] withDelegate:nil];
431 [stub lowercaseString];
432 WO_TEST_DOES_NOT_THROW([stub returning:@"foo"]);
433 WO_TEST_THROWS([stub returning:@"bar"]);
435 // raise if sent new message when message previously recorded
436 stub = [WOObjectStub stubForClass:[NSString class] withDelegate:nil];
437 WO_TEST_DOES_NOT_THROW([stub lowercaseString]);
438 WO_TEST_THROWS([stub lowercaseString]);
440 // raise if recordedInvocation called but no message previously recorded
441 WO_TEST_DOES_NOT_THROW(stub = [WOObjectStub stubForClass:aClass withDelegate:nil]);
442 WO_TEST_THROWS([stub recordedInvocation]);
444 // test automatic verify on finalize
447 - (void)testStubEquality
449 WOObjectStub *stub = nil;
450 WOObjectStub *otherStub = nil;
451 Class aClass = [self class];
454 stub = [WOObjectStub stubForClass:aClass withDelegate:nil];
455 WO_TEST_EQUAL(stub, stub);
456 WO_TEST_TRUE([stub isEqual:stub]); // another way to write the same test
458 // compare against non-stub class
459 stub = [WOObjectStub stubForClass:aClass withDelegate:nil];
460 otherStub = (WOObjectStub *)@"foobar";
461 WO_TEST_NOT_EQUAL(stub, otherStub);
462 WO_TEST_FALSE([stub isEqual:otherStub]);
464 // comparison with nil
465 stub = [WOObjectStub stubForClass:aClass withDelegate:nil];
466 WO_TEST_NOT_EQUAL(stub, nil);
467 WO_TEST_NOT_EQUAL(nil, stub);
468 WO_TEST_FALSE([stub isEqual:nil]);
470 // same class, different instance
471 stub = [WOObjectStub stubForClass:aClass withDelegate:nil];
472 otherStub = [WOObjectStub stubForClass:aClass withDelegate:nil];
473 WO_TEST_EQUAL(stub, otherStub);
474 WO_TEST_TRUE([stub isEqual:otherStub]);
476 // stubs with mismatching classes
477 stub = [WOObjectStub stubForClass:aClass withDelegate:nil];
478 otherStub = [WOObjectStub stubForClass:[NSString class] withDelegate:nil];
479 WO_TEST_NOT_EQUAL(stub, otherStub);
480 WO_TEST_FALSE([stub isEqual:otherStub]);
482 // mismatching invocations
484 // mismatching return values
489 - (void)testNonExistentSelector
493 id mock = [WOObjectMock mockForClass:[NSString class]];
494 [mock setObjCTypes:@"@@:@" forSelector:@selector(totallyRandom:)];
496 // this causes a compiler warning
497 //[[[mock expect] returning:[NSValue WOTest_valueWithObject:@"bar"]] totallyRandom:@"foo"];
499 #ifdef PENDING_MOCK_REWRITE_FOR_LEOPARD
500 // so do it this way instead
501 id stub = [[mock expect] returning:[NSValue WOTest_valueWithObject:@"bar"]];
504 // NSInvalidArgumentException: *** -[NSProxy doesNotRecognizeSelector:totallyRandom:] called!
505 // Setting breakpoints reveals that the -forward:: method of WOMock is not called anymore.
506 // So will have to change the way WOMock/WOObjectMock works on Leopard.
507 objc_msgSend(stub, @selector(totallyRandom:), @"foo");
509 // likewise, this causes a warning
510 //WO_TEST_EQ([mock totallyRandom:@"foo"], @"bar");
512 // so do it like this:
513 WO_TEST_EQ(objc_msgSend(mock, @selector(totallyRandom:), @"foo"), @"bar");
514 #endif /* PENDING_MOCK_REWRITE_FOR_LEOPARD */
517 // methods for finding out what's going on in margs_list
518 - (void)testStructReturn
523 - (void)testStructParameter
528 - (void)testFloatParameter