Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registrating stubs per Nocilla instance. #119

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Nocilla.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
A085B83B15E30749007D33C1 /* Foundation.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A085B83F15E30749007D33C1 /* Nocilla-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Nocilla-Prefix.pch"; sourceTree = "<group>"; };
A085B84015E30749007D33C1 /* Nocilla.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Nocilla.h; sourceTree = "<group>"; };
A085B84915E30749007D33C1 /* NocillaTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = NocillaTests.octest; path = NocillaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A085B84915E30749007D33C1 /* NocillaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NocillaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A085B85415E30749007D33C1 /* NocillaTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NocillaTests-Info.plist"; sourceTree = "<group>"; };
A085B85615E30749007D33C1 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
A085B85915E30749007D33C1 /* AFNetworkingStubbingSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AFNetworkingStubbingSpec.m; path = Hooks/NSURLRequest/AFNetworkingStubbingSpec.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -405,7 +405,7 @@
isa = PBXGroup;
children = (
A085B83815E30749007D33C1 /* libNocilla.a */,
A085B84915E30749007D33C1 /* NocillaTests.octest */,
A085B84915E30749007D33C1 /* NocillaTests.xctest */,
);
name = Products;
sourceTree = "<group>";
Expand Down Expand Up @@ -600,7 +600,7 @@
);
name = NocillaTests;
productName = NocillaTests;
productReference = A085B84915E30749007D33C1 /* NocillaTests.octest */;
productReference = A085B84915E30749007D33C1 /* NocillaTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
Expand Down
13 changes: 11 additions & 2 deletions Nocilla.xcodeproj/xcshareddata/xcschemes/Nocilla.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A085B84815E30749007D33C1"
BuildableName = "NocillaTests.octest"
BuildableName = "NocillaTests.xctest"
BlueprintName = "NocillaTests"
ReferencedContainer = "container:Nocilla.xcodeproj">
</BuildableReference>
Expand All @@ -47,7 +47,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A085B84815E30749007D33C1"
BuildableName = "NocillaTests.octest"
BuildableName = "NocillaTests.xctest"
BlueprintName = "NocillaTests"
ReferencedContainer = "container:Nocilla.xcodeproj">
</BuildableReference>
Expand All @@ -63,6 +63,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A085B83715E30749007D33C1"
BuildableName = "libNocilla.a"
BlueprintName = "Nocilla"
ReferencedContainer = "container:Nocilla.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
Expand Down
1 change: 1 addition & 0 deletions Nocilla/DSL/LSStubRequestDSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@class LSStubRequestDSL;
@class LSStubResponseDSL;
@class LSStubRequest;
@class LSNocilla;

@protocol LSHTTPBody;

Expand Down
4 changes: 4 additions & 0 deletions Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#import <Foundation/Foundation.h>
#import "LSNocilla.h"

@interface LSHTTPStubURLProtocol : NSURLProtocol

+ (void)registerNocilla:(LSNocilla *)nocilla;
+ (Class)randomSubclass;

@end
43 changes: 42 additions & 1 deletion Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,70 @@
#import "NSURLRequest+LSHTTPRequest.h"
#import "LSStubRequest.h"
#import "NSURLRequest+DSL.h"
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface NSHTTPURLResponse(UndocumentedInitializer)
- (id)initWithURL:(NSURL*)URL statusCode:(NSInteger)statusCode headerFields:(NSDictionary*)headerFields requestTime:(double)requestTime;
@end

@implementation LSHTTPStubURLProtocol

static NSMapTable *nocillaWeakMapTable;

+ (void)load
{
if (!nocillaWeakMapTable) {
nocillaWeakMapTable = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsCopyIn
valueOptions:NSPointerFunctionsWeakMemory];
}
}

+ (Class)randomSubclass
{
Class baseClass = [self class];
NSString * className = NSStringFromClass(baseClass);

NSString * subclassName = [NSString stringWithFormat:@"%@%d", className, arc4random()];
Class subclass = NSClassFromString(subclassName);

if (subclass == nil) {
subclass = objc_allocateClassPair(baseClass, [subclassName UTF8String], 0);
if (subclass != nil) {
objc_registerClassPair(subclass);
}
}
return subclass;
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return [@[ @"http", @"https" ] containsObject:request.URL.scheme];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return NO;
}

+ (void)registerNocilla:(LSNocilla *)nocilla
{
NSString *className = NSStringFromClass([self class]);
[nocillaWeakMapTable setObject:nocilla forKey:className];
}

+ (LSNocilla *)nocilla
{
return [nocillaWeakMapTable objectForKey:(NSStringFromClass([self class]))];
}

- (void)startLoading {
NSURLRequest* request = [self request];
id<NSURLProtocolClient> client = [self client];

LSStubResponse* stubbedResponse = [[LSNocilla sharedInstance] responseForRequest:request];
LSStubResponse* stubbedResponse = [[[self class] nocilla] responseForRequest:request];

if (stubbedResponse.shouldFail) {
[client URLProtocol:self didFailWithError:stubbedResponse.error];
Expand Down
2 changes: 2 additions & 0 deletions Nocilla/Hooks/NSURLRequest/LSNSURLHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

@interface LSNSURLHook : LSHTTPClientHook

@property(strong, nonatomic) Class urlProtocolClass;

@end
4 changes: 2 additions & 2 deletions Nocilla/Hooks/NSURLRequest/LSNSURLHook.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
@implementation LSNSURLHook

- (void)load {
[NSURLProtocol registerClass:[LSHTTPStubURLProtocol class]];
[NSURLProtocol registerClass:self.urlProtocolClass];
}

- (void)unload {
[NSURLProtocol unregisterClass:[LSHTTPStubURLProtocol class]];
[NSURLProtocol unregisterClass:self.urlProtocolClass];
}

@end
2 changes: 2 additions & 0 deletions Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@

@interface LSNSURLSessionHook : LSHTTPClientHook

@property(strong, nonatomic) Class urlProtocolClass;

@end
9 changes: 6 additions & 3 deletions Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#import "LSHTTPStubURLProtocol.h"
#import <objc/runtime.h>

@interface LSNSURLSessionHook ()

@end

@implementation LSNSURLSessionHook

- (void)load {
Expand All @@ -23,7 +27,6 @@ - (void)unload {
}

- (void)swizzleSelector:(SEL)selector fromClass:(Class)original toClass:(Class)stub {

Method originalMethod = class_getInstanceMethod(original, selector);
Method stubMethod = class_getInstanceMethod(stub, selector);
if (!originalMethod || !stubMethod) {
Expand All @@ -33,8 +36,8 @@ - (void)swizzleSelector:(SEL)selector fromClass:(Class)original toClass:(Class)s
}

- (NSArray *)protocolClasses {
return @[[LSHTTPStubURLProtocol class]];
Class stubUrlProtocolClass = self.urlProtocolClass;
return @[stubUrlProtocolClass];
}


@end
7 changes: 7 additions & 0 deletions Nocilla/LSNocilla.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@
#import "Nocilla.h"

@class LSStubRequest;
@class LSStubRequestDSL;
@class LSStubResponse;
@class LSHTTPClientHook;
@protocol LSHTTPRequest;
@protocol LSMatcheable;

extern NSString * const LSUnexpectedRequest;

typedef LSStubRequestDSL *(^StubRequestMethod)(NSString *method, id<LSMatcheable> url);

@interface LSNocilla : NSObject
+ (LSNocilla *)sharedInstance;

@property (nonatomic, strong, readonly) NSArray *stubbedRequests;
@property (nonatomic, assign, readonly, getter = isStarted) BOOL started;
@property (nonatomic, strong, readonly) StubRequestMethod stubRequest;


- (void)start;
- (void)stop;
Expand All @@ -22,4 +28,5 @@ extern NSString * const LSUnexpectedRequest;
- (void)registerHook:(LSHTTPClientHook *)hook;

- (LSStubResponse *)responseForRequest:(id<LSHTTPRequest>)request;

@end
29 changes: 26 additions & 3 deletions Nocilla/LSNocilla.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#import "LSNocilla.h"
#import "LSNSURLHook.h"
#import "LSStubRequest.h"
#import "LSStubRequestDSL.h"
#import "LSStubResponseDSL.h"
#import "LSHTTPRequestDSLRepresentation.h"
#import "LSASIHTTPRequestHook.h"
#import "LSNSURLSessionHook.h"
#import "LSASIHTTPRequestHook.h"
#import "LSHTTPStubURLProtocol.h"

NSString * const LSUnexpectedRequest = @"Unexpected Request";

Expand Down Expand Up @@ -34,9 +37,18 @@ - (id)init {
if (self) {
_mutableRequests = [NSMutableArray array];
_hooks = [NSMutableArray array];
[self registerHook:[[LSNSURLHook alloc] init]];

Class stubUrlProtocolClass = [LSHTTPStubURLProtocol randomSubclass];
[stubUrlProtocolClass registerNocilla:self];

LSNSURLHook *urlHook = [[LSNSURLHook alloc] init];
urlHook.urlProtocolClass = stubUrlProtocolClass;

[self registerHook:urlHook];
if (NSClassFromString(@"NSURLSession") != nil) {
[self registerHook:[[LSNSURLSessionHook alloc] init]];
LSNSURLSessionHook *sessionHook = [[LSNSURLSessionHook alloc] init];
sessionHook.urlProtocolClass = stubUrlProtocolClass;
[self registerHook:sessionHook];
}
[self registerHook:[[LSASIHTTPRequestHook alloc] init]];
}
Expand Down Expand Up @@ -69,7 +81,7 @@ - (void)clearStubs {
}

- (LSStubResponse *)responseForRequest:(id<LSHTTPRequest>)actualRequest {
NSArray* requests = [LSNocilla sharedInstance].stubbedRequests;
NSArray* requests = self.stubbedRequests;

for(LSStubRequest *someStubbedRequest in requests) {
if ([someStubbedRequest matchesRequest:actualRequest]) {
Expand All @@ -95,6 +107,17 @@ - (BOOL)hookWasRegistered:(LSHTTPClientHook *)aHook {
}
return NO;
}

- ( LSStubRequestDSL *(^)(NSString *method, id<LSMatcheable> url))stubRequest
{
return ^(NSString *method, id<LSMatcheable> url){
LSStubRequest *request = [[LSStubRequest alloc] initWithMethod:method urlMatcher:url.matcher];
LSStubRequestDSL *dsl = [[LSStubRequestDSL alloc] initWithRequest:request];
[self addStubbedRequest:request];
return dsl;
};
}

#pragma mark - Private
- (void)loadHooks {
for (LSHTTPClientHook *hook in self.hooks) {
Expand Down