Permalink
Browse files

Implemented mocking of messages with arguments (handling nil correctly).

  • Loading branch information...
1 parent a8b17a7 commit add7de6d848dc46d6b76ea4c78a3f86c35401a49 @robrix committed Mar 22, 2010
Showing with 123 additions and 26 deletions.
  1. +8 −2 RXAssertions.xcodeproj/project.pbxproj
  2. +2 −2 RXAssertionsTests.m
  3. +10 −3 RXMockObject.h
  4. +58 −19 RXMockObject.m
  5. +45 −0 RXMockObjectTests.m
@@ -7,8 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
- D442A48E106FD99700944F07 /* RXAssertionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D442A48D106FD99700944F07 /* RXAssertionsTests.m */; };
D442A493106FD9BF00944F07 /* libRXAssertions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2AAC07E0554694100DB518D /* libRXAssertions.a */; };
+ D472095311581618003F9546 /* RXMockObjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D472095211581618003F9546 /* RXMockObjectTests.m */; };
D4C03B4A1063445800925A75 /* RXAssertions.h in Headers */ = {isa = PBXBuildFile; fileRef = D4C03B471063445800925A75 /* RXAssertions.h */; };
D4C03B4B1063445800925A75 /* RXAssertions.m in Sources */ = {isa = PBXBuildFile; fileRef = D4C03B481063445800925A75 /* RXAssertions.m */; };
D4F3DD2811558CC2008BB0F8 /* RXMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D4F3DD2611558CC2008BB0F8 /* RXMockObject.h */; };
@@ -30,6 +30,7 @@
D442A488106FD94900944F07 /* Tests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
D442A489106FD94900944F07 /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
D442A48D106FD99700944F07 /* RXAssertionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RXAssertionsTests.m; sourceTree = "<group>"; };
+ D472095211581618003F9546 /* RXMockObjectTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RXMockObjectTests.m; sourceTree = "<group>"; };
D4C03B471063445800925A75 /* RXAssertions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RXAssertions.h; sourceTree = "<group>"; };
D4C03B481063445800925A75 /* RXAssertions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RXAssertions.m; sourceTree = "<group>"; };
D4C03B491063445800925A75 /* RXAssertions.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RXAssertions.pch; sourceTree = "<group>"; };
@@ -74,6 +75,7 @@
D442A48D106FD99700944F07 /* RXAssertionsTests.m */,
D4F3DD2611558CC2008BB0F8 /* RXMockObject.h */,
D4F3DD2711558CC2008BB0F8 /* RXMockObject.m */,
+ D472095211581618003F9546 /* RXMockObjectTests.m */,
D442A489106FD94900944F07 /* Tests-Info.plist */,
034768DFFF38A50411DB9C8B /* Products */,
);
@@ -190,7 +192,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- D442A48E106FD99700944F07 /* RXAssertionsTests.m in Sources */,
+ D472095311581618003F9546 /* RXMockObjectTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -213,6 +215,7 @@
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_ENABLE_OBJC_GC = supported;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -236,6 +239,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_ENABLE_OBJC_GC = supported;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = RXAssertions.pch;
@@ -286,6 +290,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_ENABLE_OBJC_GC = supported;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -314,6 +319,7 @@
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_ENABLE_OBJC_GC = supported;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h";
View
@@ -4,7 +4,7 @@
#import "RXAssertions.h"
-@interface RXAssertionsTests : SenTestCase {}
+@interface RXAssertionsTests : SenTestCase
@end
@implementation RXAssertionsTests
@@ -43,4 +43,4 @@ -(void)testCanAddOptionalFailureMessages {
RXAssertNotNil(object, @"This is a demonstration of an %@ failure.", @"RXAssertNotNil");
}
-@end
+@end
View
@@ -4,13 +4,20 @@
#import <Foundation/Foundation.h>
+extern NSString const *RXMockObjectNullPlaceholder;
+
@interface RXMockObject : NSObject {
NSMutableDictionary *responses;
+ Class mockedClass;
}
-+(RXMockObject *)mockObject;
-+(RXMockObject *)mockObjectRespondingToSelector:(SEL)selector withObject:(id)response;
++(RXMockObject *)mockObjectForClass:(Class)mockedClass;
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector;
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector withArgument:(id)argument;
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector withArguments:(NSArray *)arguments;
--(void)respondToSelector:(SEL)selector withObject:(id)response;
+-(void)setResponseObject:(id)object forSelector:(SEL)selector;
+-(void)setResponseObject:(id)object forSelector:(SEL)selector withArgument:(id)argument;
+-(void)setResponseObject:(id)response forSelector:(SEL)selector withArguments:(NSArray *)arguments;
@end
View
@@ -3,23 +3,16 @@
// Copyright 2010 Monochrome Industries
#import "RXMockObject.h"
+#import <objc/runtime.h>
-@implementation RXMockObject
-
-+(RXMockObject *)mockObject {
- return [[[self alloc] init] autorelease];
-}
-
-+(RXMockObject *)mockObjectRespondingToSelector:(SEL)selector withObject:(id)response {
- RXMockObject *object = [[[self alloc] init] autorelease];
- [object respondToSelector: selector withObject: response];
- return object;
-}
+NSString const *RXMockObjectNullPlaceholder = @"RXMockObjectNullPlaceholder";
+@implementation RXMockObject
--(id)init {
+-(id)initWithClass:(Class)_mockedClass {
if(self = [super init]) {
responses = [[NSMutableDictionary alloc] init];
+ mockedClass = _mockedClass;
}
return self;
}
@@ -29,28 +22,74 @@ -(void)dealloc {
[super dealloc];
}
++(RXMockObject *)mockObjectForClass:(Class)mockedClass {
+ return [[[self alloc] initWithClass: mockedClass] autorelease];
+}
--(void)respondToSelector:(SEL)selector withObject:(id)response {
- [responses setObject: response forKey: NSStringFromSelector(selector)];
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector {
+ RXMockObject *object = [[[self alloc] initWithClass: mockedClass] autorelease];
+ [object setResponseObject: response forSelector: selector];
+ return object;
}
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector withArgument:(id)argument {
+ RXMockObject *object = [[[self alloc] initWithClass: mockedClass] autorelease];
+ [object setResponseObject: response forSelector: selector withArgument: argument];
+ return object;
+}
--(id)responseForSelector:(SEL)selector {
++(RXMockObject *)mockObjectForClass:(Class)mockedClass withResponseObject:(id)response forSelector:(SEL)selector withArguments:(NSArray *)arguments {
+ RXMockObject *object = [[[self alloc] initWithClass: mockedClass] autorelease];
+ [object setResponseObject: response forSelector: selector withArguments: arguments];
+ return object;
+}
+
+
+-(void)setResponseObject:(id)response forSelector:(SEL)selector {
+ [self setResponseObject: response forSelector: selector withArguments: [NSArray array]];
+
+}
+
+-(void)setResponseObject:(id)response forSelector:(SEL)selector withArgument:(id)argument {
+ [self setResponseObject: response forSelector: selector withArguments: [NSArray arrayWithObject: argument ?: RXMockObjectNullPlaceholder]];
+}
+
+-(void)setResponseObject:(id)response forSelector:(SEL)selector withArguments:(NSArray *)arguments {
+ NSMutableDictionary *responsesByArguments = [responses objectForKey: NSStringFromSelector(selector)];
+ if(!responsesByArguments) {
+ [responses setObject: (responsesByArguments = [NSMutableDictionary dictionary]) forKey: NSStringFromSelector(selector)];
+ }
+ [responsesByArguments setObject: response forKey: arguments];
+}
+
+-(id)responsesForSelector:(SEL)selector {
return [responses objectForKey: NSStringFromSelector(selector)];
}
+-(id)responseForSelector:(SEL)selector withArguments:(NSArray *)arguments {
+ return [[self responsesForSelector: selector] objectForKey: arguments];
+}
+
-(NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
NSMethodSignature *signature = nil;
- if([self responseForSelector: selector]) {
- signature = [super methodSignatureForSelector: @selector(self)];
+ if([self responsesForSelector: selector]) {
+ signature = [mockedClass instanceMethodSignatureForSelector: selector];
}
return signature;
}
-(void)forwardInvocation:(NSInvocation *)invocation {
- id response = [self responseForSelector: invocation.selector];
- [invocation setReturnValue: &response];
+ if([self responsesForSelector: invocation.selector]) {
+ NSMutableArray *arguments = [NSMutableArray array];
+ for(NSUInteger i = 2; i < invocation.methodSignature.numberOfArguments; i++) {
+ id argument;
+ [invocation getArgument: &argument atIndex: i];
+ [arguments addObject: argument ?: RXMockObjectNullPlaceholder];
+ }
+ id response = [self responseForSelector: invocation.selector withArguments: [arguments copy]];
+ [invocation setReturnValue: &response];
+ }
}
@end
View
@@ -0,0 +1,45 @@
+// RXMockObjectTests.m
+// Created by Rob Rix on 2010-03-22
+// Copyright 2010 Monochrome Industries
+
+#import "RXAssertions.h"
+#import "RXMockObject.h"
+
+@interface NSObject (RXMockObjectTests)
+-(id)response:(id)ignored;
+@end
+
+@interface RXMockObjectTests : SenTestCase {
+ id mock;
+}
+@end
+
+@implementation RXMockObjectTests
+
+-(void)setUp {
+ mock = [RXMockObject mockObjectForClass: [NSArray class]];
+}
+
+-(void)testRespondsToNullaryMessagesWithTheGivenObject {
+ [mock setResponseObject: @"nullary response" forSelector: @selector(lastObject)];
+ RXAssertEquals([mock lastObject], @"nullary response");
+}
+
+-(void)testRespondsToUnaryMessagesWithTheGivenObject {
+ [mock setResponseObject: @"result 1" forSelector: @selector(arrayByAddingObject:) withArgument: @"argument 1"];
+ [mock setResponseObject: @"result 2" forSelector: @selector(arrayByAddingObject:) withArgument: @"argument 2"];
+ RXAssertEquals([mock arrayByAddingObject: @"argument 1"], @"result 1");
+ RXAssertEquals([mock arrayByAddingObject: @"argument 2"], @"result 2");
+ RXAssertNil([mock arrayByAddingObject: @"not specified"]);
+ RXAssertNil([mock arrayByAddingObject: nil]);
+}
+
+-(void)testRespondsToMessagesWithNilArguments {
+ [mock setResponseObject: @"result 1" forSelector: @selector(arrayByAddingObject:) withArgument: nil];
+ [mock setResponseObject: @"result 2" forSelector: @selector(arrayByAddingObject:) withArgument: @"argument 2"];
+ RXAssertEquals([mock arrayByAddingObject: nil], @"result 1");
+ RXAssertEquals([mock arrayByAddingObject: @"argument 2"], @"result 2");
+ RXAssertNil([mock arrayByAddingObject: @"not specified"]);
+}
+
+@end

0 comments on commit add7de6

Please sign in to comment.