Skip to content

Commit

Permalink
Integrating feature/namedStubs
Browse files Browse the repository at this point in the history
  • Loading branch information
AliSoftware committed Sep 27, 2013
2 parents 964ace6 + 4c54ddc commit 1fbd379
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 83 deletions.
49 changes: 37 additions & 12 deletions OHHTTPStubs/OHHTTPStubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@

typedef BOOL(^OHHTTPStubsTestBlock)(NSURLRequest* request);
typedef OHHTTPStubsResponse*(^OHHTTPStubsResponseBlock)(NSURLRequest* request);
typedef id OHHTTPStubsID;

@protocol OHHTTPStubsDescriptor
/*! Arbitrary name that you can set and get to describe your stub. Use it as your own convenience. */
@property(nonatomic, strong) NSString* name;
@end

////////////////////////////////////////////////////////////////////////////////
#pragma mark - Interface
Expand All @@ -46,18 +50,23 @@ typedef id OHHTTPStubsID;
#pragma mark - Class Methods

/*! Dedicated method to add a stub
@param testBlock Block that should return `YES` if the request passed as parameter should be stubbed with the response block, `NO` if it should hit the real world (or be managed by another stub).
@param testBlock Block that should return `YES` if the request passed as parameter should be stubbed with the response block,
and `NO` if it should hit the real world (or be managed by another stub).
@param responseBlock Block that will return the `OHHTTPStubsResponse` (response to use for stubbing) corresponding to the given request
@return an opaque object that uniquely identifies the stub and can be later used to remove it with `removeStub:`
@return a stub descriptor that uniquely identifies the stub and can be later used to remove it with `removeStub:`.
@note The returned stub descriptor is retained (`__strong` reference) by `OHHTTPStubs` until it is removed
(with one of the `removeStub:`/`removeLastStub`/`removeAllStubs` methods); it is thus recommended to
keep it in a `__weak` storage (and not `__strong`) in your app code, to let the stub descriptor be destroyed
and let the variable go back to `nil` automatically when the stub is removed.
*/
+(OHHTTPStubsID)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock
withStubResponse:(OHHTTPStubsResponseBlock)responseBlock;
+(id<OHHTTPStubsDescriptor>)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock
withStubResponse:(OHHTTPStubsResponseBlock)responseBlock;

/*! Remove a stub from the list of stubs
@param stubID the opaque object that has been returned when adding the stub using `stubRequestsPassingTest:withStubResponse:`
@param stubDesc the stub descriptor that has been returned when adding the stub using `stubRequestsPassingTest:withStubResponse:`
@return `YES` if the stub has been successfully removed, `NO` if the parameter was not a valid stub identifier
*/
+(BOOL)removeStub:(OHHTTPStubsID)stubID;
+(BOOL)removeStub:(id<OHHTTPStubsDescriptor>)stubDesc;

/*! Remove the last added stub from the stubs list */
+(void)removeLastStub;
Expand All @@ -70,6 +79,20 @@ typedef id OHHTTPStubsID;
*/
+(void)setEnabled:(BOOL)enabled;

#pragma mark - Debug Methods

/*! List all the installed stubs
@return An array of id<OHHTTPStubsDescriptor> objects currently installed. Useful for debug.
*/
+(NSArray*)allStubs;

/*! Setup a block to be called each time a stub is triggered.
Useful if you want to log all your requests being stubbed for example and see which stub was used to respond to each request.
@param block The block to call each time a request is being stubbed by OHHTTPStubs. Set it to `nil` to do nothing. Defaults is `nil`.
*/
+(void)onStubActivation:( void(^)(NSURLRequest* request, id<OHHTTPStubsDescriptor> stub) )block;

@end


Expand All @@ -80,7 +103,9 @@ typedef id OHHTTPStubsID;

@interface OHHTTPStubs (Deprecated)

typedef id OHHTTPStubsRequestHandlerID __attribute__((deprecated("Use OHHTTPStubsID instead")));
typedef id OHHTTPStubsRequestHandlerID __deprecated_msg("Use OHHTTPStubsDescriptor* instead");
typedef id<OHHTTPStubsDescriptor> OHHTTPStubsID __deprecated_msg("Use id<OHHTTPStubsDescriptor> instead");


/*! @warning This method is deprecated
Expand All @@ -92,22 +117,22 @@ typedef id OHHTTPStubsRequestHandlerID __attribute__((deprecated("Use OHHTTPStub
@return an opaque object that uniquely identifies the handler and can be later used to remove it with `removeRequestHandler:`
*/
+(OHHTTPStubsRequestHandlerID)addRequestHandler:(OHHTTPStubsResponse*(^)(NSURLRequest* request, BOOL onlyCheck))handler
__attribute__((deprecated("Use stubRequestsPassingTest:withStubResponse: instead")));
__deprecated_msg("Use stubRequestsPassingTest:withStubResponse: instead");

/*! Remove a request handler from the list of stubs
@param handlerID the opaque object that has been returned when adding the handler using `stubRequestsPassingTest:withStubResponse:`
or using `addRequestHandler:`
@return `YES` if the request handler has been successfully removed, `NO` if the parameter was not a valid handler identifier
*/
+(BOOL)removeRequestHandler:(OHHTTPStubsRequestHandlerID)handlerID
__attribute__((deprecated("Use removeStub: instead")));
__deprecated_msg("Use removeStub: instead");

/*! Remove the last added request handler from the stubs list */
+(void)removeLastRequestHandler
__attribute__((deprecated("Use removeLastStub instead")));
__deprecated_msg("Use removeLastStub instead");

/*! Remove all the requests handlers from the stubs list. */
+(void)removeAllRequestHandlers
__attribute__((deprecated("Use removeAllStubs instead")));
__deprecated_msg("Use removeAllStubs instead");

@end
154 changes: 101 additions & 53 deletions OHHTTPStubs/OHHTTPStubs.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,51 @@
#pragma mark - Types & Constants

@interface OHHTTPStubsProtocol : NSURLProtocol @end
typedef OHHTTPStubsResponse*(^OHHTTPStubsRequestHandler)(NSURLRequest* request, BOOL onlyCheck);

static NSTimeInterval const kSlotTime = 0.25; // Must be >0. We will send a chunk of the data from the stream each 'slotTime' seconds

////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private Interface
#pragma mark - Private Interfaces

@interface OHHTTPStubs()
+ (instancetype)sharedInstance;
@property(atomic, strong) NSMutableArray* requestHandlers;
@property(atomic, copy) NSMutableArray* stubDescriptors;
@property(atomic, copy) void (^onStubActivationBlock)(NSURLRequest*, id<OHHTTPStubsDescriptor>);
@end

@interface OHHTTPStubsDescriptor : NSObject <OHHTTPStubsDescriptor>
@property(atomic, copy) OHHTTPStubsTestBlock testBlock;
@property(atomic, copy) OHHTTPStubsResponseBlock responseBlock;
@end

////////////////////////////////////////////////////////////////////////////////
#pragma mark - OHHTTPStubsDescriptor Implementation

@implementation OHHTTPStubsDescriptor

@synthesize name = _name;

+(instancetype)stubDescriptorWithTestBlock:(OHHTTPStubsTestBlock)testBlock
responseBlock:(OHHTTPStubsResponseBlock)responseBlock
{
OHHTTPStubsDescriptor* stub = [OHHTTPStubsDescriptor new];
stub.testBlock = testBlock;
stub.responseBlock = responseBlock;
return stub;
}

-(NSString*)description
{
return [NSString stringWithFormat:@"<%@ %p : %@>", self.class, self, self.name];
}

@end




////////////////////////////////////////////////////////////////////////////////
#pragma mark - Implementation
#pragma mark - OHHTTPStubs Implementation

@implementation OHHTTPStubs

Expand All @@ -76,7 +107,7 @@ - (id)init
self = [super init];
if (self)
{
_requestHandlers = [NSMutableArray array];
_stubDescriptors = [NSMutableArray array];
[self.class setEnabled:YES];
}
return self;
Expand All @@ -90,34 +121,26 @@ - (void)dealloc
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Public class methods

+(OHHTTPStubsID)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock
withStubResponse:(OHHTTPStubsResponseBlock)responseBlock
+(id<OHHTTPStubsDescriptor>)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock
withStubResponse:(OHHTTPStubsResponseBlock)responseBlock
{
return [self.sharedInstance addRequestHandler:^OHHTTPStubsResponse *(NSURLRequest *request, BOOL onlyCheck)
{
BOOL shouldStub = testBlock ? testBlock(request) : YES;
if (onlyCheck)
{
return shouldStub ? (OHHTTPStubsResponse*)@"DummyStub" : (OHHTTPStubsResponse*)nil;
}
else
{
return (responseBlock && shouldStub) ? responseBlock(request) : nil;
}
}];
OHHTTPStubsDescriptor* stub = [OHHTTPStubsDescriptor stubDescriptorWithTestBlock:testBlock
responseBlock:responseBlock];
[OHHTTPStubs.sharedInstance addStub:stub];
return stub;
}

+(BOOL)removeStub:(OHHTTPStubsID)stubID
+(BOOL)removeStub:(id<OHHTTPStubsDescriptor>)stubDesc
{
return [self.sharedInstance removeRequestHandler:stubID];
return [OHHTTPStubs.sharedInstance removeStub:stubDesc];
}
+(void)removeLastStub
{
[self.sharedInstance removeLastRequestHandler];
[OHHTTPStubs.sharedInstance removeLastStub];
}
+(void)removeAllStubs
{
[self.sharedInstance removeAllRequestHandlers];
[OHHTTPStubs.sharedInstance removeAllStubs];
}

+(void)setEnabled:(BOOL)enabled
Expand All @@ -130,66 +153,77 @@ +(void)setEnabled:(BOOL)enabled
else if (!enabled && currentEnabledState)
{
// Force instanciate sharedInstance to avoid it being created later and this turning setEnabled to YES again
(void)self.sharedInstance; // This way if we call [setEnabled:NO] before any call to sharedInstance it will be kept disabled
(void)OHHTTPStubs.sharedInstance; // This way if we call [setEnabled:NO] before any call to sharedInstance it will be kept disabled
[NSURLProtocol unregisterClass:OHHTTPStubsProtocol.class];
}
currentEnabledState = enabled;
}

+(NSArray*)allStubs
{
return [OHHTTPStubs.sharedInstance stubDescriptors];
}

+(void)onStubActivation:( void(^)(NSURLRequest* request, id<OHHTTPStubsDescriptor> stub) )block
{
[OHHTTPStubs.sharedInstance setOnStubActivationBlock:block];
}



////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private instance methods

-(OHHTTPStubsID)addRequestHandler:(OHHTTPStubsRequestHandler)handler
-(void)addStub:(OHHTTPStubsDescriptor*)stubDesc
{
OHHTTPStubsRequestHandler handlerCopy = [handler copy];
@synchronized(_requestHandlers)
@synchronized(_stubDescriptors)
{
[_requestHandlers addObject:handlerCopy];
[_stubDescriptors addObject:stubDesc];
}
return handlerCopy;
}

-(BOOL)removeRequestHandler:(OHHTTPStubsID)stubID
-(BOOL)removeStub:(id<OHHTTPStubsDescriptor>)stubDesc
{
BOOL handlerFound = NO;
@synchronized(_requestHandlers)
@synchronized(_stubDescriptors)
{
handlerFound = [self.requestHandlers containsObject:stubID];
[_requestHandlers removeObject:stubID];
handlerFound = [_stubDescriptors containsObject:stubDesc];
[_stubDescriptors removeObject:stubDesc];
}
return handlerFound;
}
-(void)removeLastRequestHandler

-(void)removeLastStub
{
@synchronized(_requestHandlers)
@synchronized(_stubDescriptors)
{
[_requestHandlers removeLastObject];
[_stubDescriptors removeLastObject];
}
}

-(void)removeAllRequestHandlers
-(void)removeAllStubs
{
@synchronized(_requestHandlers)
@synchronized(_stubDescriptors)
{
[_requestHandlers removeAllObjects];
[_stubDescriptors removeAllObjects];
}
}

////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private methods

- (OHHTTPStubsResponse*)responseForRequest:(NSURLRequest*)request onlyCheck:(BOOL)onlyCheck
- (OHHTTPStubsDescriptor*)firstStubPassingTestForRequest:(NSURLRequest*)request
{
OHHTTPStubsResponse* response = nil;
@synchronized(_requestHandlers)
OHHTTPStubsDescriptor* foundStub = nil;
@synchronized(_stubDescriptors)
{
for(OHHTTPStubsRequestHandler handler in _requestHandlers.reverseObjectEnumerator)
for(OHHTTPStubsDescriptor* stub in _stubDescriptors.reverseObjectEnumerator)
{
response = handler(request, onlyCheck);
if (response) break;
if (stub.testBlock(request))
{
foundStub = stub;
break;
}
}
}
return response;
return foundStub;
}

@end
Expand All @@ -201,11 +235,18 @@ - (OHHTTPStubsResponse*)responseForRequest:(NSURLRequest*)request onlyCheck:(BOO
#pragma mark - Deprecated Methods (will be removed in 3.0)
/*! @name Deprecated Methods */

typedef OHHTTPStubsResponse*(^OHHTTPStubsRequestHandler)(NSURLRequest* request, BOOL onlyCheck) __deprecated;

@implementation OHHTTPStubs (Deprecated)

+(OHHTTPStubsRequestHandlerID)addRequestHandler:(OHHTTPStubsRequestHandler)handler
{
return [self.sharedInstance addRequestHandler:handler];
return [OHHTTPStubsDescriptor stubDescriptorWithTestBlock:^BOOL(NSURLRequest *request)
{
return (handler(request, YES) != nil);
} responseBlock:^OHHTTPStubsResponse *(NSURLRequest *request) {
return handler(request, NO);
}];
}

+(BOOL)removeRequestHandler:(OHHTTPStubsRequestHandlerID)handler
Expand Down Expand Up @@ -242,7 +283,7 @@ @implementation OHHTTPStubsProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
return ([OHHTTPStubs.sharedInstance responseForRequest:request onlyCheck:YES] != nil);
return ([OHHTTPStubs.sharedInstance firstStubPassingTestForRequest:request] != nil);
}

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)response client:(id<NSURLProtocolClient>)client
Expand All @@ -266,7 +307,14 @@ - (void)startLoading
NSURLRequest* request = self.request;
id<NSURLProtocolClient> client = self.client;

OHHTTPStubsResponse* responseStub = [OHHTTPStubs.sharedInstance responseForRequest:request onlyCheck:NO];
OHHTTPStubsDescriptor* stub = [OHHTTPStubs.sharedInstance firstStubPassingTestForRequest:request];
NSAssert(stub, @"At the time startLoading is called, canInitRequest should have assured that stub is != nil beforehand");
OHHTTPStubsResponse* responseStub = stub.responseBlock(request);

if (OHHTTPStubs.sharedInstance.onStubActivationBlock)
{
OHHTTPStubs.sharedInstance.onStubActivationBlock(request, stub);
}

if (responseStub.error == nil)
{
Expand Down Expand Up @@ -456,7 +504,7 @@ - (void) streamDataForClient:(id<NSURLProtocolClient>)client
// Delayed execution utility methods
/////////////////////////////////////////////

void execute_after(NSTimeInterval delayInSeconds, dispatch_block_t block)
static void execute_after(NSTimeInterval delayInSeconds, dispatch_block_t block)
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
Expand Down
4 changes: 2 additions & 2 deletions OHHTTPStubs/OHHTTPStubsResponse+HTTPMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
*/
+(instancetype)responseWithHTTPMessageData:(NSData*)responseData
responseTime:(NSTimeInterval)responseTime
__attribute__((deprecated("Use responseWithHTTPMessageData: and requestTime:responseTime: instead")));
__deprecated_msg("Use responseWithHTTPMessageData: and requestTime:responseTime: instead");

/*! @warning This method is deprecated
Expand All @@ -74,7 +74,7 @@ __attribute__((deprecated("Use responseWithHTTPMessageData: and requestTime:resp
+(instancetype)responseNamed:(NSString*)responseName
fromBundle:(NSBundle*)bundle
responseTime:(NSTimeInterval)responseTime
__attribute__((deprecated("Use responseNamed:inBundle: and requestTime:responseTime: instead")));
__deprecated_msg("Use responseNamed:inBundle: and requestTime:responseTime: instead");


@end
2 changes: 1 addition & 1 deletion OHHTTPStubs/OHHTTPStubsResponse+JSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@
statusCode:(int)statusCode
responseTime:(NSTimeInterval)responseTime
headers:(NSDictionary*)httpHeaders
__attribute__((deprecated("Use responseWithJSONObject:statusCode:headers: and requestTime:responseTime: instead")));
__deprecated_msg("Use responseWithJSONObject:statusCode:headers: and requestTime:responseTime: instead");

@end
Loading

0 comments on commit 1fbd379

Please sign in to comment.