Skip to content
Browse files

Merge pull request #125 from dewind/master

Capture arguments passed to mocks
  • Loading branch information...
2 parents 1da49a1 + 0872ced commit c469c2d04c8b87ed44964528c933d014f3e31dd0 Allen Ding committed Apr 25, 2012
Showing with 165 additions and 3 deletions.
  1. +1 −0 Classes/Robot.h
  2. +6 −1 Classes/Robot.m
  3. +12 −0 Kiwi.xcodeproj/project.pbxproj
  4. +13 −0 Kiwi/KWCaptureSpy.h
  5. +47 −0 Kiwi/KWCaptureSpy.m
  6. +3 −0 Kiwi/KWMock.h
  7. +7 −0 Kiwi/KWMock.m
  8. +2 −1 Kiwi/KWObjCUtilities.h
  9. +5 −0 Kiwi/KWObjCUtilities.m
  10. +2 −1 Kiwi/Kiwi.h
  11. +67 −0 Tests/KWCaptureTest.m
View
1 Classes/Robot.h
@@ -14,4 +14,5 @@
}
+ (id)robot;
- (void)speak:(NSString *)message;
+- (void)speak:(NSString *)message afterDelay:(NSTimeInterval)delay whenDone:(void(^)(void))handler;
@end
View
7 Classes/Robot.m
@@ -16,9 +16,14 @@ + (id)robot;
return [[[self alloc] init] autorelease];
}
-- (void)speak:(NSString *)message;
+- (void)speak:(NSString *)message
{
NSLog(@"Robot says %@", message);
}
+- (void)speak:(NSString *)message afterDelay:(NSTimeInterval)delay whenDone:(void(^)(void))handler
+{
+ NSLog(@"Robot will say %@ after a %f second delay", message, delay);
+}
+
@end
View
12 Kiwi.xcodeproj/project.pbxproj
@@ -8,7 +8,10 @@
/* Begin PBXBuildFile section */
19A62264151B899D00207192 /* Galaxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 19A62263151B899C00207192 /* Galaxy.m */; };
+ 4B45D6BD1548765800793B1E /* KWCaptureSpy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B45D6BB1548765800793B1E /* KWCaptureSpy.h */; };
+ 4B45D6BE1548765800793B1E /* KWCaptureSpy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B45D6BC1548765800793B1E /* KWCaptureSpy.m */; };
4B9D040814D3EE7300707E83 /* KWExampleGroupDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A3758CDA1418CDC10051268A /* KWExampleGroupDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 4BA52D0115487F0C00FC957B /* KWCaptureTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BA52D0015487F0C00FC957B /* KWCaptureTest.m */; };
637FFB2A150513D500DBFE8F /* KWBeSubclassOfClassMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 637FFB28150513D500DBFE8F /* KWBeSubclassOfClassMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
637FFB2B150513D500DBFE8F /* KWBeSubclassOfClassMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 637FFB29150513D500DBFE8F /* KWBeSubclassOfClassMatcher.m */; };
9F4E04DC12DFB71F00A3440B /* KWFutureObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F4E04D712DFB71F00A3440B /* KWFutureObject.m */; };
@@ -254,6 +257,9 @@
28D7ACF70DDB3853001CB0EB /* KiwiViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KiwiViewController.m; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* Kiwi_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Kiwi_Prefix.pch; sourceTree = "<group>"; };
+ 4B45D6BB1548765800793B1E /* KWCaptureSpy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWCaptureSpy.h; sourceTree = "<group>"; };
+ 4B45D6BC1548765800793B1E /* KWCaptureSpy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWCaptureSpy.m; sourceTree = "<group>"; };
+ 4BA52D0015487F0C00FC957B /* KWCaptureTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWCaptureTest.m; sourceTree = "<group>"; };
637FFB28150513D500DBFE8F /* KWBeSubclassOfClassMatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KWBeSubclassOfClassMatcher.h; sourceTree = "<group>"; };
637FFB29150513D500DBFE8F /* KWBeSubclassOfClassMatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KWBeSubclassOfClassMatcher.m; sourceTree = "<group>"; };
9F324E9112DFB17200A871F1 /* Contributors.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Contributors.txt; sourceTree = "<group>"; };
@@ -650,6 +656,8 @@
F59D2C54118DB5460081755E /* NSObject+KiwiMockAdditions.m */,
F51512F1119024B100D32CD4 /* NSObject+KiwiStubAdditions.h */,
F51512F2119024B100D32CD4 /* NSObject+KiwiStubAdditions.m */,
+ 4B45D6BB1548765800793B1E /* KWCaptureSpy.h */,
+ 4B45D6BC1548765800793B1E /* KWCaptureSpy.m */,
);
name = "Mocks, Stubs, and Spying";
sourceTree = "<group>";
@@ -808,6 +816,7 @@
F5CC5B6D119798D400004E69 /* KWRealObjectSpyTest.m */,
F51A59BE1191D45500598B04 /* KWRealObjectStubTest.m */,
F5C6FB1C1177CAF00068BBC8 /* KWStubTest.m */,
+ 4BA52D0015487F0C00FC957B /* KWCaptureTest.m */,
);
name = "Mocks, Stubs, and Spying";
sourceTree = "<group>";
@@ -1006,6 +1015,7 @@
ADBF4B031511C6B300784E9E /* KWAny.h in Headers */,
A3CB75E2144C3479002D1F7A /* KWExampleSuite.h in Headers */,
4B9D040814D3EE7300707E83 /* KWExampleGroupDelegate.h in Headers */,
+ 4B45D6BD1548765800793B1E /* KWCaptureSpy.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1241,6 +1251,7 @@
A385CAF013AAC9B800DCA951 /* KWMatchers.m in Sources */,
A3CB75E3144C3479002D1F7A /* KWExampleSuite.m in Sources */,
F5FC83B611B100B100BF98A4 /* KWAny.m in Sources */,
+ 4B45D6BE1548765800793B1E /* KWCaptureSpy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1290,6 +1301,7 @@
A352EA1B12EDC8380049C691 /* KWHamcrestMatcherTest.m in Sources */,
A385CAE813AA7EA200DCA951 /* KWUserDefinedMatcherTest.m in Sources */,
19A62264151B899D00207192 /* Galaxy.m in Sources */,
+ 4BA52D0115487F0C00FC957B /* KWCaptureTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
13 Kiwi/KWCaptureSpy.h
@@ -0,0 +1,13 @@
+#import <Foundation/Foundation.h>
+#import "KWMock.h"
+#import "KWMessageSpying.h"
+
+@interface KWCaptureSpy : NSObject<KWMessageSpying> {
+ id _argument;
+ NSUInteger _argumentIndex;
+}
+
+- (id)initWithArgumentIndex:(NSUInteger)index;
+@property(nonatomic, readonly, retain) id argument;
+
+@end
View
47 Kiwi/KWCaptureSpy.m
@@ -0,0 +1,47 @@
+#import "KWCaptureSpy.h"
+#import "KWObjCUtilities.h"
+#import "NSInvocation+KiwiAdditions.h"
+#import "NSMethodSignature+KiwiAdditions.h"
+#import "KWValue.h"
+
+@implementation KWCaptureSpy
+@dynamic argument;
+
+- (id)initWithArgumentIndex:(NSUInteger)index {
+ if ((self = [super init])) {
+ _argumentIndex = index;
+ }
+ return self;
+}
+
+- (id)argument {
+ if (!_argument) {
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Argument requested has yet to be captured." userInfo:nil];
+ }
+ return [[_argument retain] autorelease];
+}
+
+- (void)object:(id)anObject didReceiveInvocation:(NSInvocation *)anInvocation {
+ if (!_argument) {
+ NSMethodSignature *signature = [anInvocation methodSignature];
+ const char *objCType = [signature messageArgumentTypeAtIndex:_argumentIndex];
+ if (KWObjCTypeIsObject(objCType)) {
+ id argument = nil;
+ [anInvocation getMessageArgument:&argument atIndex:_argumentIndex];
+ if (KWObjCTypeIsBlock(objCType)) {
+ _argument = [argument copy];
+ } else {
+ _argument = [argument retain];
+ }
+ } else {
+ NSData *data = [anInvocation messageArgumentDataAtIndex:_argumentIndex];
+ _argument = [[KWValue valueWithBytes:[data bytes] objCType:objCType] retain];
+ }
+ }
+}
+
+- (void)dealloc {
+ [_argument release];
+ [super dealloc];
+}
+@end
View
3 Kiwi/KWMock.h
@@ -8,6 +8,7 @@
#import "KWInvocationCapturer.h"
@class KWMessagePattern;
+@class KWCaptureSpy;
@protocol KWMessageSpying;
@protocol KWVerifying;
@@ -74,6 +75,8 @@
- (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern;
- (void)removeMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern;
+- (KWCaptureSpy *)captureArgument:(SEL)selector atIndex:(NSUInteger)index;
+
#pragma mark -
#pragma mark Expecting Messages
View
7 Kiwi/KWMock.m
@@ -13,6 +13,7 @@
#import "KWStub.h"
#import "KWWorkarounds.h"
#import "NSInvocation+KiwiAdditions.h"
+#import "KWCaptureSpy.h"
static NSString * const ExpectOrStubTagKey = @"ExpectOrStubTagKey";
static NSString * const StubTag = @"StubTag";
@@ -258,6 +259,12 @@ - (void)clearStubs {
#pragma mark -
#pragma mark Spying on Messages
+- (KWCaptureSpy *)captureArgument:(SEL)selector atIndex:(NSUInteger)index {
+ KWCaptureSpy *spy = [[[KWCaptureSpy alloc] initWithArgumentIndex:index] autorelease];
+ [self addMessageSpy:spy forMessagePattern:[KWMessagePattern messagePatternWithSelector:selector]];
+ return spy;
+}
+
- (void)addMessageSpy:(id<KWMessageSpying>)aSpy forMessagePattern:(KWMessagePattern *)aMessagePattern {
[self expectMessagePattern:aMessagePattern];
NSMutableArray *messagePatternSpies = [self.messageSpies objectForKey:aMessagePattern];
View
3 Kiwi/KWObjCUtilities.h
@@ -22,10 +22,11 @@ BOOL KWObjCTypeIsSelector(const char *objCType);
BOOL KWObjCTypeIsPointerToType(const char *objCType);
BOOL KWObjCTypeIsPointerLike(const char *objCType);
BOOL KWObjCTypeIsUnknown(const char *objCType);
+BOOL KWObjCTypeIsBlock(const char *objCType);
NSUInteger KWObjCTypeLength(const char *objCType);
#pragma mark -
#pragma mark Selector Utlities
-NSUInteger KWSelectorParameterCount(SEL selector);
+NSUInteger KWSelectorParameterCount(SEL selector);
View
5 Kiwi/KWObjCUtilities.m
@@ -80,6 +80,11 @@ NSUInteger KWObjCTypeLength(const char *objCType) {
return [signature methodReturnLength];
}
+BOOL KWObjCTypeIsBlock(const char *objCType) {
+ return strcmp(objCType, "@?") == 0;
+}
+
+
#pragma mark -
#pragma mark Selector Utlities
View
3 Kiwi/Kiwi.h
@@ -75,7 +75,8 @@ extern "C" {
#import "KWUserDefinedMatcher.h"
#import "KWValue.h"
#import "KWVerifying.h"
-
+#import "KWCaptureSpy.h"
+
// Public Foundation Categories
#import "NSObject+KiwiMockAdditions.h"
#import "NSObject+KiwiStubAdditions.h"
View
67 Tests/KWCaptureTest.m
@@ -0,0 +1,67 @@
+//
+// Licensed under the terms in License.txt
+//
+// Copyright 2010 Allen Ding. All rights reserved.
+//
+
+#import "Kiwi.h"
+#import "KiwiTestConfiguration.h"
+#import "TestClasses.h"
+#import "KWIntercept.h"
+#import "NSMethodSignature+KiwiAdditions.h"
+#import "KWMock.h"
+
+#if KW_TESTS_ENABLED
+
+@interface KWCaptureTest : SenTestCase
+
+@end
+
+@implementation KWCaptureTest
+
+- (void)testShouldBeAbleToCaptureBlocks {
+ id robotMock = [KWMock nullMockForClass:[Robot class]];
+ KWCaptureSpy *spy = [robotMock captureArgument:@selector(speak:afterDelay:whenDone:) atIndex:2];
+
+ __block BOOL didCall = NO;
+ [robotMock speak:@"Hello" afterDelay:2 whenDone:^{
+ didCall = YES;
+ }];
+
+ void (^block)(void) = spy.argument;
+ block();
+
+ STAssertTrue(didCall, @"Should have captured and invoked block");
+}
+
+- (void)testShouldBeAbleToCaptureObjects {
+ id robotMock = [KWMock nullMockForClass:[Robot class]];
+ KWCaptureSpy *spy = [robotMock captureArgument:@selector(speak:afterDelay:whenDone:) atIndex:0];
+
+ [robotMock speak:@"Hello" afterDelay:2 whenDone:^{}];
+
+ STAssertEqualObjects(spy.argument, @"Hello", @"Captured argument should be equal to 'Hello'");
+}
+
+- (void)testShouldBeAbleToCaptureValues {
+ id robotMock = [KWMock nullMockForClass:[Robot class]];
+ KWCaptureSpy *spy = [robotMock captureArgument:@selector(speak:afterDelay:whenDone:) atIndex:1];
+
+ [robotMock speak:@"Hello" afterDelay:2 whenDone:^{}];
+
+ STAssertEqualObjects(spy.argument, [KWValue valueWithDouble:2], @"Captured argument should be equal to '2'");
+}
+
+- (void)testShouldRaiseAnExceptionIfArgumentHasNotBeenCaptured {
+ id robotMock = [KWMock nullMockForClass:[Robot class]];
+ KWCaptureSpy *spy = [robotMock captureArgument:@selector(speak:afterDelay:whenDone:) atIndex:1];
+
+ STAssertThrows([spy argument], @"Should have raised an exception");
+}
+
+- (void)tearDown {
+ KWClearStubsAndSpies();
+}
+@end
+
+#endif

0 comments on commit c469c2d

Please sign in to comment.
Something went wrong with that request. Please try again.