Permalink
Browse files

Merge pull request #114 from nheagy/master

Stubs and Spies now work with singletons
  • Loading branch information...
2 parents 5227093 + d4e08bf commit 67d5de8df350a939f09ef694dce84017878a9d9a Allen Ding committed Mar 31, 2012
Showing with 100 additions and 7 deletions.
  1. +18 −0 Classes/Galaxy.h
  2. +36 −0 Classes/Galaxy.m
  3. +1 −0 Classes/TestClasses.h
  4. +6 −0 Kiwi.xcodeproj/project.pbxproj
  5. +22 −7 Kiwi/KWIntercept.m
  6. +17 −0 Tests/KWMockTest.m
View
@@ -0,0 +1,18 @@
+//
+// Galaxy.h
+// Kiwi
+//
+// Created by Nathan Heagy on 12-03-22.
+// Copyright (c) 2012 Allen Ding. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface Galaxy : NSObject
+
++ (id)sharedGalaxy;
+- (void)notifyPlanet:(int)planet;
+- (void)notifyEarth;
+- (NSString *)name;
+
+@end
View
@@ -0,0 +1,36 @@
+//
+// Galaxy.m
+// Kiwi
+//
+// Created by Nathan Heagy on 12-03-22.
+// Copyright (c) 2012 Allen Ding. All rights reserved.
+//
+
+#import "Galaxy.h"
+
+@implementation Galaxy
+
++ (id)sharedGalaxy
+{
+ static Galaxy *instance;
+
+ static dispatch_once_t predicate;
+ dispatch_once(&predicate, ^{
+ instance = [[Galaxy alloc] init];
+ });
+
+ return instance;
+}
+
+- (void)notifyPlanet:(int)planet {
+ NSLog(@"Planet %d was notified", planet);
+}
+- (void)notifyEarth {
+ [self notifyPlanet:1];
+}
+
+- (NSString *)name
+{
+ return @"Milky Way";
+}
+@end
View
@@ -15,4 +15,5 @@
#import "TestSpy.h"
#import "TestVerifier.h"
#import "Robot.h"
+#import "Galaxy.h"
#import "StringPrefixMatcher.h"
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 19A62264151B899D00207192 /* Galaxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 19A62263151B899C00207192 /* Galaxy.m */; };
4B9D040814D3EE7300707E83 /* KWExampleGroupDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A3758CDA1418CDC10051268A /* KWExampleGroupDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
@@ -240,6 +241,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 19A62262151B899C00207192 /* Galaxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Galaxy.h; sourceTree = "<group>"; };
+ 19A62263151B899C00207192 /* Galaxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Galaxy.m; sourceTree = "<group>"; };
1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D3623240D0F684500981E51 /* KiwiAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KiwiAppDelegate.h; sourceTree = "<group>"; };
1D3623250D0F684500981E51 /* KiwiAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KiwiAppDelegate.m; sourceTree = "<group>"; };
@@ -537,6 +540,8 @@
A3A1754012E4966F004DFD70 /* Robot.m */,
A3A1754212E496CA004DFD70 /* StringPrefixMatcher.h */,
A3A1754312E496CA004DFD70 /* StringPrefixMatcher.m */,
+ 19A62262151B899C00207192 /* Galaxy.h */,
+ 19A62263151B899C00207192 /* Galaxy.m */,
);
path = Classes;
sourceTree = "<group>";
@@ -1284,6 +1289,7 @@
A352EA0E12EDC6F20049C691 /* KWHamrestMatchingAdditions.m in Sources */,
A352EA1B12EDC8380049C691 /* KWHamcrestMatcherTest.m in Sources */,
A385CAE813AA7EA200DCA951 /* KWUserDefinedMatcherTest.m in Sources */,
+ 19A62264151B899D00207192 /* Galaxy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
@@ -16,6 +16,7 @@
#pragma mark -
#pragma mark Intercept Enabled Method Implementations
+Class KWRestoreOriginalClass(id anObject);
void KWInterceptedForwardInvocation(id anObject, SEL aSelector, NSInvocation* anInvocation);
void KWInterceptedDealloc(id anObject, SEL aSelector);
Class KWInterceptedClass(id anObject, SEL aSelector);
@@ -66,9 +67,12 @@ BOOL KWClassIsInterceptClass(Class aClass) {
return result != nil;
}
+int interceptCount = 0;
+
NSString *KWInterceptClassNameForClass(Class aClass) {
const char *className = class_getName(aClass);
- return [NSString stringWithFormat:@"%s%s", className, KWInterceptClassSuffix];
+ interceptCount++;
+ return [NSString stringWithFormat:@"%s%s%d", className, KWInterceptClassSuffix, interceptCount];
}
Class KWInterceptClassForCanonicalClass(Class canonicalClass) {
@@ -152,6 +156,13 @@ void KWSetupMethodInterceptSupport(Class interceptClass, SEL aSelector) {
#pragma mark -
#pragma mark Intercept Enabled Method Implementations
+Class KWRestoreOriginalClass(id anObject) {
+ Class interceptClass = object_getClass(anObject);
+ Class originalClass = class_getSuperclass(interceptClass);
+ anObject->isa = originalClass;
+ return interceptClass;
+}
+
void KWInterceptedForwardInvocation(id anObject, SEL aSelector, NSInvocation* anInvocation) {
NSValue *key = [NSValue valueWithNonretainedObject:anObject];
NSMutableDictionary *spyArrayDictionary = [KWMessageSpies objectForKey:key];
@@ -174,9 +185,7 @@ void KWInterceptedForwardInvocation(id anObject, SEL aSelector, NSInvocation* an
return;
}
- Class interceptClass = object_getClass(anObject);
- Class originalClass = class_getSuperclass(interceptClass);
- anObject->isa = originalClass;
+ Class interceptClass = KWRestoreOriginalClass(anObject);
[anInvocation invoke];
anObject->isa = interceptClass;
}
@@ -186,9 +195,7 @@ void KWInterceptedDealloc(id anObject, SEL aSelector) {
[KWMessageSpies removeObjectForKey:key];
[KWObjectStubs removeObjectForKey:key];
- Class interceptClass = object_getClass(anObject);
- Class originalClass = class_getSuperclass(interceptClass);
- anObject->isa = originalClass;
+ KWRestoreOriginalClass(anObject);
[anObject dealloc];
}
@@ -241,6 +248,10 @@ void KWClearObjectStubs(id anObject) {
}
void KWClearAllObjectStubs(void) {
+ for (NSValue *objectKey in KWObjectStubs) {
+ id stubbedObject = [objectKey nonretainedObjectValue];
+ KWRestoreOriginalClass(stubbedObject);
+ }
[KWObjectStubs removeAllObjects];
}
@@ -285,5 +296,9 @@ void KWClearObjectSpy(id anObject, id aSpy, KWMessagePattern *aMessagePattern) {
}
void KWClearAllMessageSpies(void) {
+ for (NSValue *objectKey in KWMessageSpies) {
+ id spiedObject = [objectKey nonretainedObjectValue];
+ KWRestoreOriginalClass(spiedObject);
+ }
[KWMessageSpies removeAllObjects];
}
View
@@ -57,6 +57,23 @@ - (void)testItShouldStubWithASelector {
STAssertEquals([mock raiseShields], NO, @"expected method to be stubbed with the correct value");
}
+- (void)testItShouldBeOkToStubOnSingletons {
+ TestSpy *firstSpy = [TestSpy testSpy];
+ KWMessagePattern *firstMessagePattern = [KWMessagePattern messagePatternWithSelector:@selector(notifyEarth)];
+ [[Galaxy sharedGalaxy] addMessageSpy:firstSpy forMessagePattern:firstMessagePattern];
+
+ KWClearAllMessageSpies();
+ KWClearAllObjectStubs();
+
+ TestSpy *secondSpy = [TestSpy testSpy];
+ KWMessagePattern *secondMessagePattern = [KWMessagePattern messagePatternWithSelector:@selector(notifyPlanet:)];
+ [[Galaxy sharedGalaxy] addMessageSpy:secondSpy forMessagePattern:secondMessagePattern];
+
+ [[Galaxy sharedGalaxy] notifyEarth];
+
+ STAssertTrue(secondSpy.wasNotified, @"expected first spy to never be called");
+}
+
- (void)testItShouldStubWithASelectorAndReturnValue {
id mock = [Cruiser mock];
[mock stub:@selector(crewComplement) andReturn:[KWValue valueWithUnsignedInt:42]];

0 comments on commit 67d5de8

Please sign in to comment.