]> git.wincent.com - WOTest.git/blob - Tests/WOObjectMockTests.m
Code clean-up for garbage collection
[WOTest.git] / Tests / WOObjectMockTests.m
1 //
2 //  WOObjectMockTests.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 29 January 2006.
6 //
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.
12 //
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.
17 //
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/>.
20 //
21
22 #import "WOObjectMockTests.h"
23
24 @implementation WOObjectMockTests
25
26 #pragma mark -
27 #pragma mark High-level tests
28
29 - (void)testMockForClass
30 {
31     // should throw if passed NULL
32     WO_TEST_THROWS([WOObjectMock mockForClass:NULL]);
33
34     // should throw if passed non-class pointer
35     WO_TEST_THROWS([WOObjectMock mockForClass:(Class)self]);
36
37     // otherwise should work
38     WO_TEST_DOES_NOT_THROW([WOObjectMock mockForClass:[self class]]);
39
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]);
44 }
45
46 - (void)testInitWithClass
47 {
48     // should throw if passed NULL
49     WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:NULL]);
50
51     // should throw if passed non-class pointer
52     WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:(Class)self]);
53
54     // otherwise should work
55     WO_TEST_DOES_NOT_THROW([[WOObjectMock alloc] initWithClass:[self class]]);
56
57     // should throw if passed a meta class
58     WO_TEST_THROWS([[WOObjectMock alloc] initWithClass:object_getClass([NSString class])]);
59 }
60
61 - (void)testMockExpectInOrder
62 {
63     // basic test
64     id mock = [WOObjectMock mockForClass:[NSString class]];
65
66     [[mock expectInOrder] lowercaseString];
67     [[mock expectInOrder] uppercaseString];
68     [[mock expectInOrder] stringByExpandingTildeInPath];
69     [[mock expectInOrder] uppercaseString];
70
71     [mock lowercaseString];
72     [mock uppercaseString];
73     [mock stringByExpandingTildeInPath];
74     [mock uppercaseString];
75
76     WO_TEST_DOES_NOT_THROW([mock verify]);
77
78     // repeat test: this time omit one of the expected methods
79     [mock clear];
80     [[mock expectInOrder] lowercaseString];
81     [[mock expectInOrder] uppercaseString];
82     [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
83     [[mock expectInOrder] uppercaseString];
84
85     [mock lowercaseString];
86     [mock uppercaseString];
87     [mock stringByAbbreviatingWithTildeInPath];
88
89     WO_TEST_THROWS([mock verify]);
90
91     // repeat test: this time invoke methods in wrong order
92     [mock clear];
93     [[mock expectInOrder] lowercaseString];
94     [[mock expectInOrder] uppercaseString];
95     [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
96     [[mock expectInOrder] uppercaseString];
97
98     [mock lowercaseString];
99     WO_TEST_THROWS([mock stringByAbbreviatingWithTildeInPath]);
100
101     // test with arguments
102     [mock clear];
103     [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
104     [[mock expectInOrder] uppercaseString];
105     [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
106     [[mock expectInOrder] uppercaseString];
107
108     [mock stringByAppendingFormat:@"foobar"];
109     [mock uppercaseString];
110     [mock stringByAbbreviatingWithTildeInPath];
111     [mock uppercaseString];
112
113     WO_TEST_DOES_NOT_THROW([mock verify]);
114
115     // repeat test: this time pass unexpected argument
116     [mock clear];
117     [[mock expectInOrder] stringByAppendingFormat:@"foobar"];
118     [[mock expectInOrder] uppercaseString];
119     [[mock expectInOrder] stringByAbbreviatingWithTildeInPath];
120     [[mock expectInOrder] uppercaseString];
121
122     WO_TEST_THROWS([mock stringByAppendingFormat:@"other"]);
123
124     // test with object return value
125     NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
126     [mock clear];
127     [[[mock expectInOrder] returning:value] stringByAppendingString:@"bar"];
128     WO_TEST_EQ(@"foobar", [mock stringByAppendingString:@"bar"]);
129     WO_TEST_DOES_NOT_THROW([mock verify]);
130
131     // test with scalar return value
132     value = [NSValue WOTest_valueWithUnsignedInt:6];
133     [mock clear];
134     [[[mock expectInOrder] returning:value] length];
135     WO_TEST_EQ((unsigned int)6, [mock length]);
136     WO_TEST_DOES_NOT_THROW([mock verify]);
137
138     // test with raising exception
139     [mock clear];
140     [[[mock expectInOrder] raising:self] lowercaseString];
141     WO_TEST_THROWS([mock lowercaseString]);
142
143     // test raising named exception
144     NSException *exception = [NSException exceptionWithName:@"Robert"
145                                                      reason:@"Robert exception"
146                                                    userInfo:nil];
147     [mock clear];
148     [[[mock expectInOrder] raising:exception] lowercaseString];
149     WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
150 }
151
152 - (void)testMockExpectOnce
153 {
154     // basic test
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]);
161
162     // test with arguments
163     [mock clear];
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]);
169
170     // repeat test: this time pass unexpected argument
171     [mock clear];
172     [[mock expectOnce] stringByAppendingFormat:@"foo"];
173     WO_TEST_THROWS([mock verify]);
174     WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
175     WO_TEST_THROWS([mock verify]);
176
177     // test with return value
178     NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
179     [mock clear];
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]);
184
185     // test with raising exception
186     [mock clear];
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]);
191
192     // test raising named exception
193     NSException *exception = [NSException exceptionWithName:@"Robert"
194                                                      reason:@"Robert exception"
195                                                    userInfo:nil];
196     [mock clear];
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]);
201 }
202
203 - (void)testMockExpect
204 {
205     // basic test
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]);
211
212     // test with return value (no parameters)
213     NSValue *value = [NSValue WOTest_valueWithObject:@".txt"];
214     [mock clear];
215     [[[mock expect] returning:value] stringByAppendingPathExtension:@"txt"];
216     WO_TEST_EQ([mock stringByAppendingPathExtension:@"txt"], @".txt");
217     WO_TEST_DOES_NOT_THROW([mock verify]);
218
219     // test with parameters and return value
220     [mock clear];
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
225
226     // test with parameters but without return value
227     [mock clear];
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"]);
232 }
233
234 - (void)testMockAcceptOnce
235 {
236     // basic test
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]);
241
242     // test with arguments
243     [mock clear];
244     [[mock acceptOnce] stringByAppendingFormat:@"foo"];
245     WO_TEST_DOES_NOT_THROW([mock stringByAppendingFormat:@"foo"]);
246     WO_TEST_THROWS([mock stringByAppendingFormat:@"foo"]);
247
248     // repeat test: this time pass unexpected argument
249     [mock clear];
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"]);
254
255     // test with return value
256     NSValue *value = [NSValue WOTest_valueWithObject:@"foobar"];
257     [mock clear];
258     [[[mock acceptOnce] returning:value] stringByAppendingFormat:@"bar"];
259     WO_TEST_EQ([mock stringByAppendingFormat:@"bar"], @"foobar");
260     WO_TEST_THROWS([mock stringByAppendingFormat:@"bar"]);
261
262     // test raising exception
263     [mock clear];
264     [[[mock acceptOnce] raising:@"foo"] lowercaseString];
265     WO_TEST_THROWS([mock lowercaseString]);
266
267     // test raising named exception
268     NSException *exception = [NSException exceptionWithName:@"Robert"
269                                                      reason:@"Robert exception"
270                                                    userInfo:nil];
271     [mock clear];
272     [[[mock acceptOnce] raising:exception] lowercaseString];
273     WO_TEST_THROWS_EXCEPTION_NAMED([mock lowercaseString], @"Robert");
274 }
275
276 - (void)testMockAccept
277 {
278     // preliminaries
279     id mock = nil;
280
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"]);
286
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)
293
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"]);
298 }
299
300 - (void)testClear
301 {
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
307     [mock clear];
308     WO_TEST_THROWS([mock lowercaseString]);         // but fail on clear
309
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]);
313     [mock clear];
314     [[mock accept] lowercaseString];
315     WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
316     [mock clear];
317
318     // same for acceptOnce
319     [[mock acceptOnce] lowercaseString];
320     [mock clear];
321     WO_TEST_THROWS([mock lowercaseString]);
322
323     // expect
324     [[mock expect] lowercaseString];
325     [mock clear];
326     WO_TEST_THROWS([mock lowercaseString]);
327
328     // expectOnce
329     [[mock expectOnce] lowercaseString];
330     [mock clear];
331     WO_TEST_THROWS([mock lowercaseString]);
332
333     // expectInOrder
334     [[mock expectInOrder] lowercaseString];
335     [mock clear];
336     WO_TEST_THROWS([mock lowercaseString]);
337 }
338
339 - (void)testAnyArguments
340 {
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]);
347
348     // but should also be able to accept any argument
349     [mock clear];
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]);
355 }
356
357 - (void)testAcceptsByDefault
358 {
359     id mock = [WOObjectMock mockForClass:[NSString class]];
360     [mock setAcceptsByDefault:YES];
361     WO_TEST_DOES_NOT_THROW([mock lowercaseString]);
362
363     // unrecognised selector should always throw
364     WO_TEST_THROWS(objc_msgSend(mock, @selector(foobar)));
365
366     [mock setAcceptsByDefault:NO];
367     WO_TEST_THROWS([mock lowercaseString]);
368 }
369
370 - (void)testReturning
371 {
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);
379 }
380
381 #pragma mark -
382 #pragma mark Low-level tests
383
384 - (void)testMock
385 {
386     // preliminaries
387     Class aClass = [self class];
388
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]);
394
395     // test if passed a non-class object (ie. an instance)
396
397     //
398 }
399
400 - (void)testObjectStub
401 {
402     // preliminaries
403     id      stub    = nil;
404     Class   aClass  = [self class];
405
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]);
411
412     // test if passed a non-class object (ie. an instance)
413
414
415
416     // shouldn't crash even if passed non-NSObject descendant
417
418
419     // test hashes
420
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);
425
426     // not with subclasses either
427
428
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"]);
434
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]);
439
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]);
443
444     // test automatic verify on finalize
445 }
446
447 - (void)testStubEquality
448 {
449     WOObjectStub *stub        = nil;
450     WOObjectStub *otherStub   = nil;
451     Class   aClass      = [self class];
452
453     // pointer equality
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
457
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]);
463
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]);
469
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]);
475
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]);
481
482     // mismatching invocations
483
484     // mismatching return values
485
486
487 }
488
489 - (void)testNonExistentSelector
490 {
491     WO_TEST_START;
492
493     id mock = [WOObjectMock mockForClass:[NSString class]];
494     [mock setObjCTypes:@"@@:@" forSelector:@selector(totallyRandom:)];
495
496     // this causes a compiler warning
497     //[[[mock expect] returning:[NSValue WOTest_valueWithObject:@"bar"]] totallyRandom:@"foo"];
498
499 #ifdef PENDING_MOCK_REWRITE_FOR_LEOPARD
500     // so do it this way instead
501     id stub = [[mock expect] returning:[NSValue WOTest_valueWithObject:@"bar"]];
502
503     // Here we die with:
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");
508
509     // likewise, this causes a warning
510     //WO_TEST_EQ([mock totallyRandom:@"foo"], @"bar");
511
512     // so do it like this:
513     WO_TEST_EQ(objc_msgSend(mock, @selector(totallyRandom:), @"foo"), @"bar");
514 #endif /* PENDING_MOCK_REWRITE_FOR_LEOPARD */
515 }
516
517 // methods for finding out what's going on in margs_list
518 - (void)testStructReturn
519 {
520
521 }
522
523 - (void)testStructParameter
524 {
525
526 }
527
528 - (void)testFloatParameter
529 {
530
531 }
532
533 @end