├── .gitignore
├── .ruby-version
├── .travis.yml
├── .xctool-args
├── LICENSE
├── Nocilla.podspec
├── Nocilla.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── Nocilla Mac.xcscheme
│ ├── Nocilla tvOS.xcscheme
│ └── Nocilla.xcscheme
├── Nocilla.xcworkspace
└── contents.xcworkspacedata
├── Nocilla
├── Categories
│ ├── NSData+Nocilla.h
│ ├── NSData+Nocilla.m
│ ├── NSString+Nocilla.h
│ └── NSString+Nocilla.m
├── DSL
│ ├── LSHTTPRequestDSLRepresentation.h
│ ├── LSHTTPRequestDSLRepresentation.m
│ ├── LSStubRequestDSL.h
│ ├── LSStubRequestDSL.m
│ ├── LSStubResponseDSL.h
│ └── LSStubResponseDSL.m
├── Diff
│ ├── LSHTTPRequestDiff.h
│ └── LSHTTPRequestDiff.m
├── Hooks
│ ├── ASIHTTPRequest
│ │ ├── ASIHTTPRequestStub.h
│ │ ├── ASIHTTPRequestStub.m
│ │ ├── LSASIHTTPRequestAdapter.h
│ │ ├── LSASIHTTPRequestAdapter.m
│ │ ├── LSASIHTTPRequestHook.h
│ │ └── LSASIHTTPRequestHook.m
│ ├── LSHTTPClientHook.h
│ ├── LSHTTPClientHook.m
│ ├── NSURLRequest
│ │ ├── LSHTTPStubURLProtocol.h
│ │ ├── LSHTTPStubURLProtocol.m
│ │ ├── LSNSURLHook.h
│ │ ├── LSNSURLHook.m
│ │ ├── NSURLRequest+DSL.h
│ │ ├── NSURLRequest+DSL.m
│ │ ├── NSURLRequest+LSHTTPRequest.h
│ │ └── NSURLRequest+LSHTTPRequest.m
│ └── NSURLSession
│ │ ├── LSNSURLSessionHook.h
│ │ └── LSNSURLSessionHook.m
├── Info.plist
├── LSNocilla.h
├── LSNocilla.m
├── Matchers
│ ├── LSDataMatcher.h
│ ├── LSDataMatcher.m
│ ├── LSMatcheable.h
│ ├── LSMatcher.h
│ ├── LSMatcher.m
│ ├── LSRegexMatcher.h
│ ├── LSRegexMatcher.m
│ ├── LSStringMatcher.h
│ ├── LSStringMatcher.m
│ ├── NSData+Matcheable.h
│ ├── NSData+Matcheable.m
│ ├── NSRegularExpression+Matcheable.h
│ ├── NSRegularExpression+Matcheable.m
│ ├── NSString+Matcheable.h
│ └── NSString+Matcheable.m
├── Model
│ ├── LSHTTPBody.h
│ ├── LSHTTPRequest.h
│ └── LSHTTPResponse.h
├── Nocilla-Prefix.pch
├── Nocilla.h
└── Stubs
│ ├── LSStubRequest.h
│ ├── LSStubRequest.m
│ ├── LSStubResponse.h
│ └── LSStubResponse.m
├── NocillaTests
├── DSL
│ └── LSHTTPRequestDSLRepresentationSpec.m
├── Diff
│ └── LSHTTPRequestDiffSpec.m
├── Hooks
│ ├── ASIHTTPRequest
│ │ ├── ASIHTTPRequestStubbingSpec.m
│ │ └── LSASIHTTPRequestHookSpec.m
│ ├── NSURLRequest
│ │ ├── AFNetworkingStubbingSpec.m
│ │ ├── LSHTTPStubURLProtocolSpec.m
│ │ ├── MKNetworkKitStubbingSpec.m
│ │ ├── NSURLRequest+LSHTTPRequestSpec.m
│ │ └── NSURLRequestHookSpec.m
│ └── NSURLSession
│ │ └── NSURLSessionStubbingSpec.m
├── Info.plist
├── LSNocillaSpec.m
├── LSStubResponseSpec.m
├── LSTestRequest.h
├── LSTestRequest.m
├── Matchers
│ ├── LSDataMatcherSpec.m
│ ├── LSRegexMatcherSpec.m
│ └── LSStringMatcherSpec.m
├── NocillaTests-Info.plist
├── Stubs
│ └── LSStubRequestSpec.m
├── Support
│ ├── LSTestingConnection.h
│ └── LSTestingConnection.m
└── en.lproj
│ └── InfoPlist.strings
├── Podfile
├── Podfile.lock
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | build/*
3 | *.pbxuser
4 | !default.pbxuser
5 | *.mode1v3
6 | !default.mode1v3
7 | *.mode2v3
8 | !default.mode2v3
9 | *.perspectivev3
10 | !default.perspectivev3
11 | *.xcworkspace
12 | !default.xcworkspace
13 | xcuserdata
14 | profile
15 | *.moved-aside
16 | .DS_Store
17 | Pods
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.2.2
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: objective-c
3 |
4 | before_install:
5 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
6 | - gem install cocoapods --no-rdoc --no-ri --no-document --quiet
7 |
8 | install:
9 | - pod repo remove master
10 | - pod setup
11 | - pod install
12 |
13 | script:
14 | - set -o pipefail && xcodebuild -workspace Nocilla.xcworkspace -scheme 'Nocilla' -sdk iphonesimulator build test CODE_SIGN_IDENTITY=- | xcpretty -c
15 |
--------------------------------------------------------------------------------
/.xctool-args:
--------------------------------------------------------------------------------
1 | [
2 | "-workspace", "Nocilla.xcworkspace",
3 | "-scheme", "Nocilla",
4 | ]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Luis Solano Bonet
2 | MIT License
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Nocilla.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Nocilla"
3 | s.version = "0.11.0"
4 | s.summary = "Stunning HTTP stubbing for iOS. Testing HTTP requests has never been easier."
5 | s.homepage = "https://github.com/luisobo/Nocilla"
6 |
7 | s.license = { :type => 'MIT', :file => 'LICENSE' }
8 | s.author = { "Luis Solano" => "contact@luissolano.com" }
9 |
10 | s.source = { :git => "https://github.com/luisobo/Nocilla.git", :tag => "0.11.0" }
11 |
12 | s.ios.deployment_target = '5.0'
13 | s.osx.deployment_target = '10.7'
14 | s.tvos.deployment_target = '9.0'
15 |
16 | s.source_files = 'Nocilla/**/*.{h,m}'
17 |
18 | s.public_header_files = [
19 | 'Nocilla/Categories/NSData+Nocilla.h',
20 | 'Nocilla/Categories/NSString+Nocilla.h',
21 | 'Nocilla/DSL/LSStubRequestDSL.h',
22 | 'Nocilla/DSL/LSStubResponseDSL.h',
23 | 'Nocilla/LSNocilla.h',
24 | 'Nocilla/Matchers/LSMatcheable.h',
25 | 'Nocilla/Matchers/LSMatcher.h',
26 | 'Nocilla/Matchers/NSData+Matcheable.h',
27 | 'Nocilla/Matchers/NSRegularExpression+Matcheable.h',
28 | 'Nocilla/Matchers/NSString+Matcheable.h',
29 | 'Nocilla/Model/LSHTTPBody.h',
30 | 'Nocilla/Nocilla.h'
31 | ]
32 |
33 | s.requires_arc = true
34 | s.frameworks = 'CFNetwork'
35 | end
36 |
--------------------------------------------------------------------------------
/Nocilla.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Nocilla.xcodeproj/xcshareddata/xcschemes/Nocilla Mac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Nocilla.xcodeproj/xcshareddata/xcschemes/Nocilla tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Nocilla.xcodeproj/xcshareddata/xcschemes/Nocilla.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
69 |
70 |
76 |
77 |
78 |
79 |
83 |
84 |
85 |
86 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Nocilla.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Nocilla/Categories/NSData+Nocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPBody.h"
3 |
4 | @interface NSData (Nocilla)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/Categories/NSData+Nocilla.m:
--------------------------------------------------------------------------------
1 | #import "NSData+Nocilla.h"
2 |
3 | @implementation NSData (Nocilla)
4 |
5 | - (NSData *)data {
6 | return self;
7 | }
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Nocilla/Categories/NSString+Nocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPBody.h"
3 |
4 | @interface NSString (Nocilla)
5 |
6 | - (NSRegularExpression *)regex;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/Nocilla/Categories/NSString+Nocilla.m:
--------------------------------------------------------------------------------
1 | #import "NSString+Nocilla.h"
2 |
3 | @implementation NSString (Nocilla)
4 |
5 | - (NSRegularExpression *)regex {
6 | NSError *error = nil;
7 | NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:self options:0 error:&error];
8 | if (error) {
9 | [NSException raise:NSInvalidArgumentException format:@"Invalid regex pattern: %@\nError: %@", self, error];
10 | }
11 | return regex;
12 | }
13 |
14 | - (NSData *)data {
15 | return [self dataUsingEncoding:NSUTF8StringEncoding];
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSHTTPRequestDSLRepresentation.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface LSHTTPRequestDSLRepresentation : NSObject
5 | - (id)initWithRequest:(id)request;
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSHTTPRequestDSLRepresentation.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPRequestDSLRepresentation.h"
2 |
3 | @interface LSHTTPRequestDSLRepresentation ()
4 | @property (nonatomic, strong) id request;
5 | @end
6 |
7 | @implementation LSHTTPRequestDSLRepresentation
8 | - (id)initWithRequest:(id)request {
9 | self = [super init];
10 | if (self) {
11 | _request = request;
12 | }
13 | return self;
14 | }
15 |
16 | - (NSString *)description {
17 | NSMutableString *result = [NSMutableString stringWithFormat:@"stubRequest(@\"%@\", @\"%@\")", self.request.method, [self.request.url absoluteString]];
18 | if (self.request.headers.count) {
19 | [result appendString:@".\nwithHeaders(@{ "];
20 | NSMutableArray *headerElements = [NSMutableArray arrayWithCapacity:self.request.headers.count];
21 |
22 | NSArray *descriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]];
23 | NSArray * sortedHeaders = [[self.request.headers allKeys] sortedArrayUsingDescriptors:descriptors];
24 |
25 | for (NSString * header in sortedHeaders) {
26 | NSString *value = [self.request.headers objectForKey:header];
27 | [headerElements addObject:[NSString stringWithFormat:@"@\"%@\": @\"%@\"", header, value]];
28 | }
29 | [result appendString:[headerElements componentsJoinedByString:@", "]];
30 | [result appendString:@" })"];
31 | }
32 | if (self.request.body.length) {
33 | NSString *escapedBody = [[NSString alloc] initWithData:self.request.body encoding:NSUTF8StringEncoding];
34 | escapedBody = [escapedBody stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
35 | [result appendFormat:@".\nwithBody(@\"%@\")", escapedBody];
36 | }
37 | return [NSString stringWithFormat:@"%@;", result];
38 | }
39 | @end
40 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSStubRequestDSL.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "NSString+Matcheable.h"
3 | #import "NSRegularExpression+Matcheable.h"
4 | #import "NSData+Matcheable.h"
5 |
6 | @class LSStubRequestDSL;
7 | @class LSStubResponseDSL;
8 | @class LSStubRequest;
9 |
10 | @protocol LSHTTPBody;
11 |
12 | typedef LSStubRequestDSL *(^WithHeaderMethod)(NSString *, NSString *);
13 | typedef LSStubRequestDSL *(^WithHeadersMethod)(NSDictionary *);
14 | typedef LSStubRequestDSL *(^AndBodyMethod)(id);
15 | typedef LSStubResponseDSL *(^AndReturnMethod)(NSInteger);
16 | typedef LSStubResponseDSL *(^AndReturnRawResponseMethod)(NSData *rawResponseData);
17 | typedef void (^AndFailWithErrorMethod)(NSError *error);
18 |
19 | @interface LSStubRequestDSL : NSObject
20 | - (id)initWithRequest:(LSStubRequest *)request;
21 |
22 | @property (nonatomic, strong, readonly) WithHeaderMethod withHeader;
23 | @property (nonatomic, strong, readonly) WithHeadersMethod withHeaders;
24 | @property (nonatomic, strong, readonly) AndBodyMethod withBody;
25 | @property (nonatomic, strong, readonly) AndReturnMethod andReturn;
26 | @property (nonatomic, strong, readonly) AndReturnRawResponseMethod andReturnRawResponse;
27 | @property (nonatomic, strong, readonly) AndFailWithErrorMethod andFailWithError;
28 |
29 | @end
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 |
35 | LSStubRequestDSL * stubRequest(NSString *method, id url);
36 |
37 | #ifdef __cplusplus
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSStubRequestDSL.m:
--------------------------------------------------------------------------------
1 | #import "LSStubRequestDSL.h"
2 | #import "LSStubResponseDSL.h"
3 | #import "LSStubRequest.h"
4 | #import "LSNocilla.h"
5 |
6 | @interface LSStubRequestDSL ()
7 | @property (nonatomic, strong) LSStubRequest *request;
8 | @end
9 |
10 | @implementation LSStubRequestDSL
11 |
12 | - (id)initWithRequest:(LSStubRequest *)request {
13 | self = [super init];
14 | if (self) {
15 | _request = request;
16 | }
17 | return self;
18 | }
19 | - (WithHeadersMethod)withHeaders {
20 | return ^(NSDictionary *headers) {
21 | for (NSString *header in headers) {
22 | NSString *value = [headers objectForKey:header];
23 | [self.request setHeader:header value:value];
24 | }
25 | return self;
26 | };
27 | }
28 |
29 | - (WithHeaderMethod)withHeader {
30 | return ^(NSString * header, NSString * value) {
31 | [self.request setHeader:header value:value];
32 | return self;
33 | };
34 | }
35 |
36 | - (AndBodyMethod)withBody {
37 | return ^(id body) {
38 | self.request.body = body.matcher;
39 | return self;
40 | };
41 | }
42 |
43 | - (AndReturnMethod)andReturn {
44 | return ^(NSInteger statusCode) {
45 | self.request.response = [[LSStubResponse alloc] initWithStatusCode:statusCode];
46 | LSStubResponseDSL *responseDSL = [[LSStubResponseDSL alloc] initWithResponse:self.request.response];
47 | return responseDSL;
48 | };
49 | }
50 |
51 | - (AndReturnRawResponseMethod)andReturnRawResponse {
52 | return ^(NSData *rawResponseData) {
53 | self.request.response = [[LSStubResponse alloc] initWithRawResponse:rawResponseData];
54 | LSStubResponseDSL *responseDSL = [[LSStubResponseDSL alloc] initWithResponse:self.request.response];
55 | return responseDSL;
56 | };
57 | }
58 |
59 | - (AndFailWithErrorMethod)andFailWithError {
60 | return ^(NSError *error) {
61 | self.request.response = [[LSStubResponse alloc] initWithError:error];
62 | };
63 | }
64 |
65 | @end
66 |
67 | LSStubRequestDSL * stubRequest(NSString *method, id url) {
68 | LSStubRequest *request = [[LSStubRequest alloc] initWithMethod:method urlMatcher:url.matcher];
69 | LSStubRequestDSL *dsl = [[LSStubRequestDSL alloc] initWithRequest:request];
70 | [[LSNocilla sharedInstance] addStubbedRequest:request];
71 | return dsl;
72 | }
73 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSStubResponseDSL.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class LSStubResponse;
4 | @class LSStubResponseDSL;
5 |
6 | @protocol LSHTTPBody;
7 |
8 | typedef LSStubResponseDSL *(^ResponseWithBodyMethod)(id);
9 | typedef LSStubResponseDSL *(^ResponseWithHeaderMethod)(NSString *, NSString *);
10 | typedef LSStubResponseDSL *(^ResponseWithHeadersMethod)(NSDictionary *);
11 |
12 | @interface LSStubResponseDSL : NSObject
13 | - (id)initWithResponse:(LSStubResponse *)response;
14 |
15 | @property (nonatomic, strong, readonly) ResponseWithHeaderMethod withHeader;
16 | @property (nonatomic, strong, readonly) ResponseWithHeadersMethod withHeaders;
17 | @property (nonatomic, strong, readonly) ResponseWithBodyMethod withBody;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Nocilla/DSL/LSStubResponseDSL.m:
--------------------------------------------------------------------------------
1 | #import "LSStubResponseDSL.h"
2 | #import "LSStubResponse.h"
3 | #import "LSHTTPBody.h"
4 |
5 | @interface LSStubResponseDSL ()
6 | @property (nonatomic, strong) LSStubResponse *response;
7 | @end
8 |
9 | @implementation LSStubResponseDSL
10 | - (id)initWithResponse:(LSStubResponse *)response {
11 | self = [super init];
12 | if (self) {
13 | _response = response;
14 | }
15 | return self;
16 | }
17 | - (ResponseWithHeaderMethod)withHeader {
18 | return ^(NSString * header, NSString * value) {
19 | [self.response setHeader:header value:value];
20 | return self;
21 | };
22 | }
23 |
24 | - (ResponseWithHeadersMethod)withHeaders; {
25 | return ^(NSDictionary *headers) {
26 | for (NSString *header in headers) {
27 | NSString *value = [headers objectForKey:header];
28 | [self.response setHeader:header value:value];
29 | }
30 | return self;
31 | };
32 | }
33 |
34 | - (ResponseWithBodyMethod)withBody {
35 | return ^(id body) {
36 | self.response.body = [body data];
37 | return self;
38 | };
39 | }
40 | @end
41 |
--------------------------------------------------------------------------------
/Nocilla/Diff/LSHTTPRequestDiff.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface LSHTTPRequestDiff : NSObject
5 | @property (nonatomic, assign, readonly, getter = isEmpty) BOOL empty;
6 |
7 | - (id)initWithRequest:(id)oneRequest andRequest:(id)anotherRequest;
8 | @end
9 |
--------------------------------------------------------------------------------
/Nocilla/Diff/LSHTTPRequestDiff.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPRequestDiff.h"
2 |
3 | @interface LSHTTPRequestDiff ()
4 | @property (nonatomic, strong) idoneRequest;
5 | @property (nonatomic, strong) idanotherRequest;
6 |
7 | - (BOOL)isMethodDifferent;
8 | - (BOOL)isUrlDifferent;
9 | - (BOOL)areHeadersDifferent;
10 | - (BOOL)isBodyDifferent;
11 |
12 | - (void)appendMethodDiff:(NSMutableString *)diff;
13 | - (void)appendUrlDiff:(NSMutableString *)diff;
14 | - (void)appendHeadersDiff:(NSMutableString *)diff;
15 | - (void)appendBodyDiff:(NSMutableString *)diff;
16 | @end
17 |
18 | @implementation LSHTTPRequestDiff
19 | - (id)initWithRequest:(id)oneRequest andRequest:(id)anotherRequest {
20 | self = [super init];
21 | if (self) {
22 | _oneRequest = oneRequest;
23 | _anotherRequest = anotherRequest;
24 | }
25 | return self;
26 | }
27 |
28 | - (BOOL)isEmpty {
29 | if ([self isMethodDifferent] ||
30 | [self isUrlDifferent] ||
31 | [self areHeadersDifferent] ||
32 | [self isBodyDifferent]) {
33 | return NO;
34 | }
35 | return YES;
36 | }
37 |
38 | - (NSString *)description {
39 | NSMutableString *diff = [@"" mutableCopy];
40 | if ([self isMethodDifferent]) {
41 | [self appendMethodDiff:diff];
42 | }
43 | if ([self isUrlDifferent]) {
44 | [self appendUrlDiff:diff];
45 | }
46 | if([self areHeadersDifferent]) {
47 | [self appendHeadersDiff:diff];
48 | }
49 | if([self isBodyDifferent]) {
50 | [self appendBodyDiff:diff];
51 | }
52 | return [NSString stringWithString:diff];
53 | }
54 |
55 | #pragma mark - Private Methods
56 | - (BOOL)isMethodDifferent {
57 | return ![self.oneRequest.method isEqualToString:self.anotherRequest.method];
58 | }
59 |
60 | - (BOOL)isUrlDifferent {
61 | return ![self.oneRequest.url isEqual:self.anotherRequest.url];
62 | }
63 |
64 | - (BOOL)areHeadersDifferent {
65 | return ![self.oneRequest.headers isEqual:self.anotherRequest.headers];
66 | }
67 |
68 | - (BOOL)isBodyDifferent {
69 | return (((self.oneRequest.body) && (![self.oneRequest.body isEqual:self.anotherRequest.body])) ||
70 | ((self.anotherRequest.body) && (![self.anotherRequest.body isEqual:self.oneRequest.body])));
71 | }
72 |
73 | - (void)appendMethodDiff:(NSMutableString *)diff {
74 | [diff appendFormat:@"- Method: %@\n+ Method: %@\n", self.oneRequest.method, self.anotherRequest.method];
75 | }
76 |
77 | - (void)appendUrlDiff:(NSMutableString *)diff {
78 | [diff appendFormat:@"- URL: %@\n+ URL: %@\n", [self.oneRequest.url absoluteString], [self.anotherRequest.url absoluteString]];
79 | }
80 |
81 | - (void)appendHeadersDiff:(NSMutableString *)diff {
82 | [diff appendString:@" Headers:\n"];
83 | NSSet *headersInOneButNotInTheOther = [self.oneRequest.headers keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
84 | return ![self.anotherRequest.headers objectForKey:key] || ![obj isEqual:[self.anotherRequest.headers objectForKey:key]];
85 | }];
86 | NSSet *headersInTheOtherButNotInOne = [self.anotherRequest.headers keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
87 | return ![self.oneRequest.headers objectForKey:key] || ![obj isEqual:[self.oneRequest.headers objectForKey:key]];
88 | }];
89 |
90 | NSArray *descriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]];
91 | NSArray * sortedHeadersInOneButNotInTheOther = [headersInOneButNotInTheOther sortedArrayUsingDescriptors:descriptors];
92 | NSArray * sortedHeadersInTheOtherButNotInOne = [headersInTheOtherButNotInOne sortedArrayUsingDescriptors:descriptors];
93 | for (NSString *header in sortedHeadersInOneButNotInTheOther) {
94 | NSString *value = [self.oneRequest.headers objectForKey:header];
95 | [diff appendFormat:@"-\t\"%@\": \"%@\"\n", header, value];
96 |
97 | }
98 | for (NSString *header in sortedHeadersInTheOtherButNotInOne) {
99 | NSString *value = [self.anotherRequest.headers objectForKey:header];
100 | [diff appendFormat:@"+\t\"%@\": \"%@\"\n", header, value];
101 | }
102 | }
103 |
104 | - (void)appendBodyDiff:(NSMutableString *)diff {
105 | NSString *oneBody = [[NSString alloc] initWithData:self.oneRequest.body encoding:NSUTF8StringEncoding];
106 | if (oneBody.length) {
107 | [diff appendFormat:@"- Body: \"%@\"\n", oneBody];
108 | }
109 | NSString *anotherBody = [[NSString alloc] initWithData:self.anotherRequest.body encoding:NSUTF8StringEncoding];
110 | if (anotherBody.length) {
111 | [diff appendFormat:@"+ Body: \"%@\"\n", anotherBody];
112 | }
113 | }
114 | @end
115 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/ASIHTTPRequestStub.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface ASIHTTPRequestStub : NSObject
4 | - (int)stub_responseStatusCode;
5 | - (NSData *)stub_responseData;
6 | - (NSDictionary *)stub_responseHeaders;
7 | - (void)stub_startRequest;
8 | @end
9 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/ASIHTTPRequestStub.m:
--------------------------------------------------------------------------------
1 | #import "ASIHTTPRequestStub.h"
2 | #import "LSStubResponse.h"
3 | #import "LSNocilla.h"
4 | #import "LSASIHTTPRequestAdapter.h"
5 | #import
6 |
7 | @interface ASIHTTPRequestStub ()
8 | @property (nonatomic, strong) LSStubResponse *stubResponse;
9 | @end
10 |
11 | @interface ASIHTTPRequestStub (Private)
12 | - (void)failWithError:(NSError *)error;
13 | - (void)requestFinished;
14 | - (void)markAsFinished;
15 | @end
16 |
17 | static void const * ASIHTTPRequestStubResponseKey = &ASIHTTPRequestStubResponseKey;
18 |
19 | @implementation ASIHTTPRequestStub
20 |
21 | - (void)setStubResponse:(LSStubResponse *)stubResponse {
22 | objc_setAssociatedObject(self, ASIHTTPRequestStubResponseKey, stubResponse, OBJC_ASSOCIATION_RETAIN);
23 | }
24 |
25 | - (LSStubResponse *)stubResponse {
26 | return objc_getAssociatedObject(self, ASIHTTPRequestStubResponseKey);
27 | }
28 |
29 | - (int)stub_responseStatusCode {
30 | return (int)self.stubResponse.statusCode;
31 | }
32 |
33 | - (NSData *)stub_responseData {
34 | return self.stubResponse.body;
35 | }
36 |
37 | - (NSDictionary *)stub_responseHeaders {
38 | return self.stubResponse.headers;
39 | }
40 |
41 | - (void)stub_startRequest {
42 | self.stubResponse = [[LSNocilla sharedInstance] responseForRequest:[[LSASIHTTPRequestAdapter alloc] initWithASIHTTPRequest:(id)self]];
43 |
44 | if (self.stubResponse.shouldFail) {
45 | [self failWithError:self.stubResponse.error];
46 | } else {
47 | [self requestFinished];
48 | }
49 | [self markAsFinished];
50 | }
51 |
52 | @end
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestAdapter.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @class ASIHTTPRequest;
5 |
6 | @interface LSASIHTTPRequestAdapter : NSObject
7 |
8 | - (instancetype)initWithASIHTTPRequest:(ASIHTTPRequest *)request;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestAdapter.m:
--------------------------------------------------------------------------------
1 | #import "LSASIHTTPRequestAdapter.h"
2 |
3 | @interface ASIHTTPRequest
4 |
5 | @property (nonatomic, strong, readonly) NSURL *url;
6 | @property (nonatomic, strong, readonly) NSString *requestMethod;
7 | @property (nonatomic, strong, readonly) NSDictionary *requestHeaders;
8 | @property (nonatomic, strong, readonly) NSData *postBody;
9 |
10 | @end
11 |
12 | @interface LSASIHTTPRequestAdapter ()
13 | @property (nonatomic, strong) ASIHTTPRequest *request;
14 | @end
15 |
16 | @implementation LSASIHTTPRequestAdapter
17 |
18 | - (instancetype)initWithASIHTTPRequest:(ASIHTTPRequest *)request {
19 | self = [super init];
20 | if (self) {
21 | _request = request;
22 | }
23 | return self;
24 | }
25 |
26 | - (NSURL *)url {
27 | return self.request.url;
28 | }
29 |
30 | - (NSString *)method {
31 | return self.request.requestMethod;
32 | }
33 |
34 | - (NSDictionary *)headers {
35 | return self.request.requestHeaders;
36 | }
37 |
38 | - (NSData *)body {
39 | return self.request.postBody;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestHook.h:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @interface LSASIHTTPRequestHook : LSHTTPClientHook
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestHook.m:
--------------------------------------------------------------------------------
1 | #import "LSASIHTTPRequestHook.h"
2 | #import "ASIHTTPRequestStub.h"
3 | #import
4 |
5 | @implementation LSASIHTTPRequestHook
6 |
7 | - (void)load {
8 | if (!NSClassFromString(@"ASIHTTPRequest")) return;
9 | [self swizzleASIHTTPRequest];
10 | }
11 |
12 | - (void)unload {
13 | if (!NSClassFromString(@"ASIHTTPRequest")) return;
14 | [self swizzleASIHTTPRequest];
15 | }
16 |
17 | #pragma mark - Internal Methods
18 |
19 | - (void)swizzleASIHTTPRequest {
20 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseStatusCode") withSelector:@selector(stub_responseStatusCode)];
21 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseData") withSelector:@selector(stub_responseData)];
22 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseHeaders") withSelector:@selector(stub_responseHeaders)];
23 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"startRequest") withSelector:@selector(stub_startRequest)];
24 | [self addMethodToASIHTTPRequest:NSSelectorFromString(@"stubResponse")];
25 | [self addMethodToASIHTTPRequest:NSSelectorFromString(@"setStubResponse:")];
26 | }
27 |
28 | - (void)swizzleASIHTTPSelector:(SEL)original withSelector:(SEL)stub {
29 | Class asiHttpRequest = NSClassFromString(@"ASIHTTPRequest");
30 | Method originalMethod = class_getInstanceMethod(asiHttpRequest, original);
31 | Method stubMethod = class_getInstanceMethod([ASIHTTPRequestStub class], stub);
32 | if (!originalMethod || !stubMethod) {
33 | [self fail];
34 | }
35 | method_exchangeImplementations(originalMethod, stubMethod);
36 | }
37 |
38 | - (void)addMethodToASIHTTPRequest:(SEL)newMethod {
39 | Method method = class_getInstanceMethod([ASIHTTPRequestStub class], newMethod);
40 | const char *types = method_getTypeEncoding(method);
41 | class_addMethod(NSClassFromString(@"ASIHTTPRequest"), newMethod, class_getMethodImplementation([ASIHTTPRequestStub class], newMethod), types);
42 | }
43 |
44 | - (void)fail {
45 | [NSException raise:NSInternalInconsistencyException format:@"Couldn't load ASIHTTPRequest hook."];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/LSHTTPClientHook.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSHTTPClientHook : NSObject
4 | - (void)load;
5 | - (void)unload;
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/LSHTTPClientHook.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @implementation LSHTTPClientHook
4 | - (void)load {
5 | [NSException raise:NSInternalInconsistencyException
6 | format:@"Method '%@' not implemented. Subclass '%@' and override it", NSStringFromSelector(_cmd), NSStringFromClass([self class])];
7 | }
8 |
9 | - (void)unload {
10 | [NSException raise:NSInternalInconsistencyException
11 | format:@"Method '%@' not implemented. Subclass '%@' and override it", NSStringFromSelector(_cmd), NSStringFromClass([self class])];
12 | }
13 | @end
14 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSHTTPStubURLProtocol : NSURLProtocol
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPStubURLProtocol.h"
2 | #import "LSNocilla.h"
3 | #import "NSURLRequest+LSHTTPRequest.h"
4 | #import "LSStubRequest.h"
5 | #import "NSURLRequest+DSL.h"
6 |
7 | @implementation LSHTTPStubURLProtocol
8 |
9 | + (BOOL)canInitWithRequest:(NSURLRequest *)request {
10 | return [@[ @"http", @"https" ] containsObject:request.URL.scheme];
11 | }
12 |
13 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
14 | return request;
15 | }
16 | + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
17 | return NO;
18 | }
19 |
20 | - (void)startLoading {
21 | NSURLRequest* request = [self request];
22 | id client = [self client];
23 |
24 | LSStubResponse* stubbedResponse = [[LSNocilla sharedInstance] responseForRequest:request];
25 |
26 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
27 | [cookieStorage setCookies:[NSHTTPCookie cookiesWithResponseHeaderFields:stubbedResponse.headers forURL:request.url]
28 | forURL:request.URL mainDocumentURL:request.URL];
29 |
30 | if (stubbedResponse.shouldFail) {
31 | [client URLProtocol:self didFailWithError:stubbedResponse.error];
32 | } else {
33 | NSHTTPURLResponse* urlResponse = [[NSHTTPURLResponse alloc] initWithURL:request.URL
34 | statusCode:stubbedResponse.statusCode
35 | HTTPVersion:nil
36 | headerFields:stubbedResponse.headers];
37 |
38 | if (stubbedResponse.statusCode < 300 || stubbedResponse.statusCode > 399
39 | || stubbedResponse.statusCode == 304 || stubbedResponse.statusCode == 305 ) {
40 | NSData *body = stubbedResponse.body;
41 |
42 | [client URLProtocol:self didReceiveResponse:urlResponse
43 | cacheStoragePolicy:NSURLCacheStorageNotAllowed];
44 | [client URLProtocol:self didLoadData:body];
45 | [client URLProtocolDidFinishLoading:self];
46 | } else {
47 |
48 | NSURL *newURL = [NSURL URLWithString:[stubbedResponse.headers objectForKey:@"Location"] relativeToURL:request.URL];
49 | NSMutableURLRequest *redirectRequest = [NSMutableURLRequest requestWithURL:newURL];
50 |
51 | [redirectRequest setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:[cookieStorage cookiesForURL:newURL]]];
52 |
53 | [client URLProtocol:self
54 | wasRedirectedToRequest:redirectRequest
55 | redirectResponse:urlResponse];
56 | // According to: https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Listings/CustomHTTPProtocol_Core_Code_CustomHTTPProtocol_m.html
57 | // needs to abort the original request
58 | [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
59 |
60 | }
61 | }
62 | }
63 |
64 | - (void)stopLoading {
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/LSNSURLHook.h:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @interface LSNSURLHook : LSHTTPClientHook
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/LSNSURLHook.m:
--------------------------------------------------------------------------------
1 | #import "LSNSURLHook.h"
2 | #import "LSHTTPStubURLProtocol.h"
3 |
4 | @implementation LSNSURLHook
5 |
6 | - (void)load {
7 | [NSURLProtocol registerClass:[LSHTTPStubURLProtocol class]];
8 | }
9 |
10 | - (void)unload {
11 | [NSURLProtocol unregisterClass:[LSHTTPStubURLProtocol class]];
12 | }
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/NSURLRequest+DSL.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface NSURLRequest (DSL)
4 | - (NSString *)toNocillaDSL;
5 | @end
6 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/NSURLRequest+DSL.m:
--------------------------------------------------------------------------------
1 | #import "NSURLRequest+DSL.h"
2 | #import "LSHTTPRequestDSLRepresentation.h"
3 | #import "NSURLRequest+LSHTTPRequest.h"
4 |
5 | @implementation NSURLRequest (DSL)
6 | - (NSString *)toNocillaDSL {
7 | return [[[LSHTTPRequestDSLRepresentation alloc] initWithRequest:self] description];
8 | }
9 | @end
10 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/NSURLRequest+LSHTTPRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface NSURLRequest (LSHTTPRequest)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLRequest/NSURLRequest+LSHTTPRequest.m:
--------------------------------------------------------------------------------
1 | #import "NSURLRequest+LSHTTPRequest.h"
2 |
3 | @implementation NSURLRequest (LSHTTPRequest)
4 |
5 | - (NSURL*)url {
6 | return self.URL;
7 | }
8 |
9 | - (NSString *)method {
10 | return self.HTTPMethod;
11 | }
12 |
13 | - (NSDictionary *)headers {
14 | return self.allHTTPHeaderFields;
15 | }
16 |
17 | - (NSData *)body {
18 | if (self.HTTPBodyStream) {
19 | NSInputStream *stream = self.HTTPBodyStream;
20 | NSMutableData *data = [NSMutableData data];
21 | [stream open];
22 | size_t bufferSize = 4096;
23 | uint8_t *buffer = malloc(bufferSize);
24 | if (buffer == NULL) {
25 | [NSException raise:@"NocillaMallocFailure" format:@"Could not allocate %zu bytes to read HTTPBodyStream", bufferSize];
26 | }
27 | while ([stream hasBytesAvailable]) {
28 | NSInteger bytesRead = [stream read:buffer maxLength:bufferSize];
29 | if (bytesRead > 0) {
30 | NSData *readData = [NSData dataWithBytes:buffer length:bytesRead];
31 | [data appendData:readData];
32 | } else if (bytesRead < 0) {
33 | [NSException raise:@"NocillaStreamReadError" format:@"An error occurred while reading HTTPBodyStream (%ld)", (long)bytesRead];
34 | } else if (bytesRead == 0) {
35 | break;
36 | }
37 | }
38 | free(buffer);
39 | [stream close];
40 |
41 | return data;
42 | }
43 |
44 | return self.HTTPBody;
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.h:
--------------------------------------------------------------------------------
1 | //
2 | // LSNSURLSessionHook.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 08/01/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "LSHTTPClientHook.h"
12 |
13 | @interface LSNSURLSessionHook : LSHTTPClientHook
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.m:
--------------------------------------------------------------------------------
1 | //
2 | // LSNSURLSessionHook.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 08/01/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "LSNSURLSessionHook.h"
10 | #import "LSHTTPStubURLProtocol.h"
11 | #import
12 |
13 | @implementation LSNSURLSessionHook
14 |
15 | - (void)load {
16 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
17 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];
18 | }
19 |
20 | - (void)unload {
21 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
22 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];
23 | }
24 |
25 | - (void)swizzleSelector:(SEL)selector fromClass:(Class)original toClass:(Class)stub {
26 |
27 | Method originalMethod = class_getInstanceMethod(original, selector);
28 | Method stubMethod = class_getInstanceMethod(stub, selector);
29 | if (!originalMethod || !stubMethod) {
30 | [NSException raise:NSInternalInconsistencyException format:@"Couldn't load NSURLSession hook."];
31 | }
32 | method_exchangeImplementations(originalMethod, stubMethod);
33 | }
34 |
35 | - (NSArray *)protocolClasses {
36 | return @[[LSHTTPStubURLProtocol class]];
37 | }
38 |
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/Nocilla/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Nocilla/LSNocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "Nocilla.h"
3 |
4 | @class LSStubRequest;
5 | @class LSStubResponse;
6 | @class LSHTTPClientHook;
7 | @protocol LSHTTPRequest;
8 |
9 | extern NSString * const LSUnexpectedRequest;
10 |
11 | @interface LSNocilla : NSObject
12 | + (LSNocilla *)sharedInstance;
13 |
14 | @property (nonatomic, strong, readonly) NSArray *stubbedRequests;
15 | @property (nonatomic, assign, readonly, getter = isStarted) BOOL started;
16 |
17 | - (void)start;
18 | - (void)stop;
19 | - (void)addStubbedRequest:(LSStubRequest *)request;
20 | - (void)clearStubs;
21 |
22 | - (void)registerHook:(LSHTTPClientHook *)hook;
23 |
24 | - (LSStubResponse *)responseForRequest:(id)request;
25 | @end
26 |
--------------------------------------------------------------------------------
/Nocilla/LSNocilla.m:
--------------------------------------------------------------------------------
1 | #import "LSNocilla.h"
2 | #import "LSNSURLHook.h"
3 | #import "LSStubRequest.h"
4 | #import "LSHTTPRequestDSLRepresentation.h"
5 | #import "LSASIHTTPRequestHook.h"
6 | #import "LSNSURLSessionHook.h"
7 | #import "LSASIHTTPRequestHook.h"
8 |
9 | NSString * const LSUnexpectedRequest = @"Unexpected Request";
10 |
11 | @interface LSNocilla ()
12 | @property (nonatomic, strong) NSMutableArray *mutableRequests;
13 | @property (nonatomic, strong) NSMutableArray *hooks;
14 | @property (nonatomic, assign, getter = isStarted) BOOL started;
15 |
16 | - (void)loadHooks;
17 | - (void)unloadHooks;
18 | @end
19 |
20 | static LSNocilla *sharedInstace = nil;
21 |
22 | @implementation LSNocilla
23 |
24 | + (LSNocilla *)sharedInstance {
25 | static dispatch_once_t onceToken;
26 | dispatch_once(&onceToken, ^{
27 | sharedInstace = [[self alloc] init];
28 | });
29 | return sharedInstace;
30 | }
31 |
32 | - (id)init {
33 | self = [super init];
34 | if (self) {
35 | _mutableRequests = [NSMutableArray array];
36 | _hooks = [NSMutableArray array];
37 | [self registerHook:[[LSNSURLHook alloc] init]];
38 | if (NSClassFromString(@"NSURLSession") != nil) {
39 | [self registerHook:[[LSNSURLSessionHook alloc] init]];
40 | }
41 | [self registerHook:[[LSASIHTTPRequestHook alloc] init]];
42 | }
43 | return self;
44 | }
45 |
46 | - (NSArray *)stubbedRequests {
47 | return [NSArray arrayWithArray:self.mutableRequests];
48 | }
49 |
50 | - (void)start {
51 | if (!self.isStarted){
52 | [self loadHooks];
53 | self.started = YES;
54 | }
55 | }
56 |
57 | - (void)stop {
58 | [self unloadHooks];
59 | [self clearStubs];
60 | self.started = NO;
61 | }
62 |
63 | - (void)addStubbedRequest:(LSStubRequest *)request {
64 | NSUInteger index = [self.mutableRequests indexOfObject:request];
65 |
66 | if (index == NSNotFound) {
67 | [self.mutableRequests addObject:request];
68 | return;
69 | }
70 |
71 | [self.mutableRequests replaceObjectAtIndex:index withObject:request];
72 | }
73 |
74 | - (void)clearStubs {
75 | [self.mutableRequests removeAllObjects];
76 | }
77 |
78 | - (LSStubResponse *)responseForRequest:(id)actualRequest {
79 | NSArray* requests = [LSNocilla sharedInstance].stubbedRequests;
80 |
81 | for(LSStubRequest *someStubbedRequest in requests) {
82 | if ([someStubbedRequest matchesRequest:actualRequest]) {
83 | return someStubbedRequest.response;
84 | }
85 | }
86 | [NSException raise:@"NocillaUnexpectedRequest" format:@"An unexpected HTTP request was fired.\n\nUse this snippet to stub the request:\n%@\n", [[[LSHTTPRequestDSLRepresentation alloc] initWithRequest:actualRequest] description]];
87 |
88 | return nil;
89 | }
90 |
91 | - (void)registerHook:(LSHTTPClientHook *)hook {
92 | if (![self hookWasRegistered:hook]) {
93 | [[self hooks] addObject:hook];
94 | }
95 | }
96 |
97 | - (BOOL)hookWasRegistered:(LSHTTPClientHook *)aHook {
98 | for (LSHTTPClientHook *hook in self.hooks) {
99 | if ([hook isMemberOfClass: [aHook class]]) {
100 | return YES;
101 | }
102 | }
103 | return NO;
104 | }
105 | #pragma mark - Private
106 | - (void)loadHooks {
107 | for (LSHTTPClientHook *hook in self.hooks) {
108 | [hook load];
109 | }
110 | }
111 |
112 | - (void)unloadHooks {
113 | for (LSHTTPClientHook *hook in self.hooks) {
114 | [hook unload];
115 | }
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSDataMatcher.h:
--------------------------------------------------------------------------------
1 | //
2 | // LSDataMatcher.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LSMatcher.h"
11 |
12 | @interface LSDataMatcher : LSMatcher
13 |
14 | - (instancetype)initWithData:(NSData *)data;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSDataMatcher.m:
--------------------------------------------------------------------------------
1 | //
2 | // LSDataMatcher.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "LSDataMatcher.h"
10 |
11 | @interface LSDataMatcher ()
12 |
13 | @property (nonatomic, copy) NSData *data;
14 |
15 | @end
16 |
17 | @implementation LSDataMatcher
18 |
19 | - (instancetype)initWithData:(NSData *)data {
20 | self = [super init];
21 |
22 | if (self) {
23 | _data = data;
24 | }
25 | return self;
26 | }
27 |
28 | - (BOOL)matchesData:(NSData *)data {
29 | return [self.data isEqualToData:data];
30 | }
31 |
32 |
33 | #pragma mark - Equality
34 |
35 | - (BOOL)isEqual:(id)object {
36 | if (self == object) {
37 | return YES;
38 | }
39 |
40 | if (![object isKindOfClass:[LSDataMatcher class]]) {
41 | return NO;
42 | }
43 |
44 | return [self.data isEqual:((LSDataMatcher *)object).data];
45 | }
46 |
47 | - (NSUInteger)hash {
48 | return self.data.hash;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSMatcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class LSMatcher;
4 |
5 | @protocol LSMatcheable
6 |
7 | - (LSMatcher *)matcher;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSMatcher.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSMatcher : NSObject
4 |
5 | - (BOOL)matches:(NSString *)string;
6 |
7 | - (BOOL)matchesData:(NSData *)data;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSMatcher.h"
2 |
3 | @implementation LSMatcher
4 |
5 | - (BOOL)matches:(NSString *)string {
6 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher matches:] is an abstract method" userInfo:nil];
7 | }
8 |
9 | - (BOOL)matchesData:(NSData *)data {
10 | return [self matches:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
11 | }
12 |
13 |
14 | #pragma mark - Equality
15 |
16 | - (BOOL)isEqual:(id)object {
17 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher isEqual:] is an abstract method" userInfo:nil];
18 | }
19 |
20 | - (NSUInteger)hash {
21 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher hash] an abstract method" userInfo:nil];
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSRegexMatcher.h:
--------------------------------------------------------------------------------
1 | #import "LSMatcher.h"
2 |
3 | @interface LSRegexMatcher : LSMatcher
4 |
5 | - (instancetype)initWithRegex:(NSRegularExpression *)regex;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSRegexMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSRegexMatcher.h"
2 |
3 | @interface LSRegexMatcher ()
4 | @property (nonatomic, strong) NSRegularExpression *regex;
5 | @end
6 |
7 | @implementation LSRegexMatcher
8 |
9 | - (instancetype)initWithRegex:(NSRegularExpression *)regex {
10 | self = [super init];
11 | if (self) {
12 | _regex = regex;
13 | }
14 | return self;
15 | }
16 |
17 | - (BOOL)matches:(NSString *)string {
18 | return [self.regex numberOfMatchesInString:string options:0 range:NSMakeRange(0, string.length)] > 0;
19 | }
20 |
21 |
22 | #pragma mark - Equality
23 |
24 | - (BOOL)isEqual:(id)object {
25 | if (self == object) {
26 | return YES;
27 | }
28 |
29 | if (![object isKindOfClass:[LSRegexMatcher class]]) {
30 | return NO;
31 | }
32 |
33 | return [self.regex isEqual:((LSRegexMatcher *)object).regex];
34 | }
35 |
36 | - (NSUInteger)hash {
37 | return self.regex.hash;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSStringMatcher.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcher.h"
3 |
4 | @interface LSStringMatcher : LSMatcher
5 |
6 | - (instancetype)initWithString:(NSString *)string;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/LSStringMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSStringMatcher.h"
2 |
3 | @interface LSStringMatcher ()
4 |
5 | @property (nonatomic, copy) NSString *string;
6 |
7 | @end
8 |
9 | @implementation LSStringMatcher
10 |
11 | - (instancetype)initWithString:(NSString *)string {
12 | self = [super init];
13 | if (self) {
14 | _string = string;
15 | }
16 | return self;
17 | }
18 |
19 | - (BOOL)matches:(NSString *)string {
20 | return [self.string isEqualToString:string];
21 | }
22 |
23 |
24 | #pragma mark - Equality
25 |
26 | - (BOOL)isEqual:(id)object {
27 | if (self == object) {
28 | return YES;
29 | }
30 |
31 | if (![object isKindOfClass:[LSStringMatcher class]]) {
32 | return NO;
33 | }
34 |
35 | return [self.string isEqualToString:((LSStringMatcher *)object).string];
36 | }
37 |
38 | - (NSUInteger)hash {
39 | return self.string.hash;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSData+Matcheable.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+Matcheable.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LSMatcheable.h"
11 |
12 | @interface NSData (Matcheable)
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSData+Matcheable.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+Matcheable.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "NSData+Matcheable.h"
10 | #import "LSDataMatcher.h"
11 |
12 | @implementation NSData (Matcheable)
13 |
14 | - (LSMatcher *)matcher {
15 | return [[LSDataMatcher alloc] initWithData:self];
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSRegularExpression+Matcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcheable.h"
3 |
4 | @interface NSRegularExpression (Matcheable)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSRegularExpression+Matcheable.m:
--------------------------------------------------------------------------------
1 | #import "NSRegularExpression+Matcheable.h"
2 | #import "LSRegexMatcher.h"
3 |
4 | @implementation NSRegularExpression (Matcheable)
5 |
6 | - (LSMatcher *)matcher {
7 | return [[LSRegexMatcher alloc] initWithRegex:self];
8 | }
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSString+Matcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcheable.h"
3 |
4 | @interface NSString (Matcheable)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Nocilla/Matchers/NSString+Matcheable.m:
--------------------------------------------------------------------------------
1 | #import "NSString+Matcheable.h"
2 | #import "LSStringMatcher.h"
3 |
4 | @implementation NSString (Matcheable)
5 |
6 | - (LSMatcher *)matcher {
7 | return [[LSStringMatcher alloc] initWithString:self];
8 | }
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Nocilla/Model/LSHTTPBody.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPBody
4 | - (NSData *)data;
5 | @end
6 |
--------------------------------------------------------------------------------
/Nocilla/Model/LSHTTPRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPRequest
4 |
5 | @property (nonatomic, strong, readonly) NSURL *url;
6 | @property (nonatomic, strong, readonly) NSString *method;
7 | @property (nonatomic, strong, readonly) NSDictionary *headers;
8 | @property (nonatomic, strong, readonly) NSData *body;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Nocilla/Model/LSHTTPResponse.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPResponse
4 | @property (nonatomic, assign, readonly) NSInteger statusCode;
5 | @property (nonatomic, strong, readonly) NSDictionary *headers;
6 | @property (nonatomic, strong, readonly) NSData *body;
7 | @end
8 |
--------------------------------------------------------------------------------
/Nocilla/Nocilla-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'Nocilla' target in the 'Nocilla' project
3 | //
4 |
5 | #ifdef __OBJC__
6 | #import
7 | #endif
8 |
--------------------------------------------------------------------------------
/Nocilla/Nocilla.h:
--------------------------------------------------------------------------------
1 | //
2 | // Nocilla.h
3 | // Nocilla
4 | //
5 | // Created by Robert Böhnke on 26/03/15.
6 | // Copyright (c) 2015 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Nocilla.
12 | FOUNDATION_EXPORT double NocillaVersionNumber;
13 |
14 | //! Project version string for Nocilla.
15 | FOUNDATION_EXPORT const unsigned char NocillaVersionString[];
16 |
17 | #import
18 | #import
19 | #import
20 | #import
21 | #import
22 | #import
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 |
--------------------------------------------------------------------------------
/Nocilla/Stubs/LSStubRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSStubResponse.h"
3 | #import "LSHTTPRequest.h"
4 |
5 |
6 | @class LSMatcher;
7 | @class LSStubRequest;
8 | @class LSStubResponse;
9 |
10 | @interface LSStubRequest : NSObject
11 | @property (nonatomic, strong, readonly) NSString *method;
12 | @property (nonatomic, strong, readonly) LSMatcher *urlMatcher;
13 | @property (nonatomic, strong, readonly) NSDictionary *headers;
14 | @property (nonatomic, strong, readwrite) LSMatcher *body;
15 |
16 | @property (nonatomic, strong) LSStubResponse *response;
17 |
18 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url;
19 | - (instancetype)initWithMethod:(NSString *)method urlMatcher:(LSMatcher *)urlMatcher;
20 |
21 | - (void)setHeader:(NSString *)header value:(NSString *)value;
22 |
23 | - (BOOL)matchesRequest:(id)request;
24 | @end
25 |
--------------------------------------------------------------------------------
/Nocilla/Stubs/LSStubRequest.m:
--------------------------------------------------------------------------------
1 | #import "LSStubRequest.h"
2 | #import "LSMatcher.h"
3 | #import "NSString+Matcheable.h"
4 |
5 | @interface LSStubRequest ()
6 | @property (nonatomic, strong, readwrite) NSString *method;
7 | @property (nonatomic, strong, readwrite) LSMatcher *urlMatcher;
8 | @property (nonatomic, strong, readwrite) NSMutableDictionary *mutableHeaders;
9 |
10 | -(BOOL)matchesMethod:(id)request;
11 | -(BOOL)matchesURL:(id)request;
12 | -(BOOL)matchesHeaders:(id)request;
13 | -(BOOL)matchesBody:(id)request;
14 | @end
15 |
16 | @implementation LSStubRequest
17 |
18 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url {
19 | return [self initWithMethod:method urlMatcher:[url matcher]];
20 | }
21 |
22 | - (instancetype)initWithMethod:(NSString *)method urlMatcher:(LSMatcher *)urlMatcher; {
23 | self = [super init];
24 | if (self) {
25 | self.method = method;
26 | self.urlMatcher = urlMatcher;
27 | self.mutableHeaders = [NSMutableDictionary dictionary];
28 | }
29 | return self;
30 | }
31 |
32 | - (void)setHeader:(NSString *)header value:(NSString *)value {
33 | [self.mutableHeaders setValue:value forKey:header];
34 | }
35 |
36 | - (NSDictionary *)headers {
37 | return [NSDictionary dictionaryWithDictionary:self.mutableHeaders];;
38 | }
39 |
40 | - (NSString *)description {
41 | return [NSString stringWithFormat:@"StubRequest:\nMethod: %@\nURL: %@\nHeaders: %@\nBody: %@\nResponse: %@",
42 | self.method,
43 | self.urlMatcher,
44 | self.headers,
45 | self.body,
46 | self.response];
47 | }
48 |
49 | - (LSStubResponse *)response {
50 | if (!_response) {
51 | _response = [[LSStubResponse alloc] initDefaultResponse];
52 | }
53 | return _response;
54 |
55 | }
56 |
57 | - (BOOL)matchesRequest:(id)request {
58 | if ([self matchesMethod:request]
59 | && [self matchesURL:request]
60 | && [self matchesHeaders:request]
61 | && [self matchesBody:request]
62 | ) {
63 | return YES;
64 | }
65 | return NO;
66 | }
67 |
68 | -(BOOL)matchesMethod:(id)request {
69 | if (!self.method || [self.method isEqualToString:request.method]) {
70 | return YES;
71 | }
72 | return NO;
73 | }
74 |
75 | -(BOOL)matchesURL:(id)request {
76 | return [self.urlMatcher matches:[request.url absoluteString]];
77 | }
78 |
79 | -(BOOL)matchesHeaders:(id)request {
80 | for (NSString *header in self.headers) {
81 | if (![[request.headers objectForKey:header] isEqualToString:[self.headers objectForKey:header]]) {
82 | return NO;
83 | }
84 | }
85 | return YES;
86 | }
87 |
88 | -(BOOL)matchesBody:(id)request {
89 | NSData *reqBody = request.body;
90 | if (!self.body || [self.body matchesData:reqBody]) {
91 | return YES;
92 | }
93 | return NO;
94 | }
95 |
96 |
97 | #pragma mark - Equality
98 |
99 | - (BOOL)isEqual:(id)object {
100 | if (self == object) {
101 | return YES;
102 | }
103 |
104 | if (![object isKindOfClass:[LSStubRequest class]]) {
105 | return NO;
106 | }
107 |
108 | return [self isEqualToStubRequest:object];
109 | }
110 |
111 | - (BOOL)isEqualToStubRequest:(LSStubRequest *)stubRequest {
112 | if (!stubRequest) {
113 | return NO;
114 | }
115 |
116 | BOOL methodEqual = [self.method isEqualToString:stubRequest.method];
117 | BOOL urlMatcherEqual = [self.urlMatcher isEqual:stubRequest.urlMatcher];
118 | BOOL headersEqual = [self.headers isEqual:stubRequest.headers];
119 | BOOL bodyEqual = (self.body == nil && stubRequest.body == nil) || [self.body isEqual:stubRequest.body];
120 |
121 | return methodEqual && urlMatcherEqual && headersEqual && bodyEqual;
122 | }
123 |
124 | - (NSUInteger)hash {
125 | return self.method.hash ^ self.urlMatcher.hash ^ self.headers.hash ^ self.body.hash;
126 | }
127 |
128 | @end
129 |
130 |
131 |
--------------------------------------------------------------------------------
/Nocilla/Stubs/LSStubResponse.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPResponse.h"
3 |
4 | @interface LSStubResponse : NSObject
5 |
6 | @property (nonatomic, assign, readonly) NSInteger statusCode;
7 | @property (nonatomic, strong) NSData *body;
8 | @property (nonatomic, strong, readonly) NSDictionary *headers;
9 |
10 | @property (nonatomic, assign, readonly) BOOL shouldFail;
11 | @property (nonatomic, strong, readonly) NSError *error;
12 |
13 | - (id)initWithError:(NSError *)error;
14 | - (id)initWithStatusCode:(NSInteger)statusCode;
15 | - (id)initWithRawResponse:(NSData *)rawResponseData;
16 | - (id)initDefaultResponse;
17 | - (void)setHeader:(NSString *)header value:(NSString *)value;
18 | @end
19 |
--------------------------------------------------------------------------------
/Nocilla/Stubs/LSStubResponse.m:
--------------------------------------------------------------------------------
1 | #import "LSStubResponse.h"
2 |
3 | @interface LSStubResponse ()
4 | @property (nonatomic, assign, readwrite) NSInteger statusCode;
5 | @property (nonatomic, strong) NSMutableDictionary *mutableHeaders;
6 | @property (nonatomic, assign) UInt64 offset;
7 | @property (nonatomic, assign, getter = isDone) BOOL done;
8 | @property (nonatomic, assign) BOOL shouldFail;
9 | @property (nonatomic, strong) NSError *error;
10 | @end
11 |
12 | @implementation LSStubResponse
13 |
14 | #pragma Initializers
15 | - (id)initDefaultResponse {
16 | self = [super init];
17 | if (self) {
18 | self.shouldFail = NO;
19 |
20 | self.statusCode = 200;
21 | self.mutableHeaders = [NSMutableDictionary dictionary];
22 | self.body = [@"" dataUsingEncoding:NSUTF8StringEncoding];
23 | }
24 | return self;
25 | }
26 |
27 |
28 | - (id)initWithError:(NSError *)error {
29 | self = [super init];
30 | if (self) {
31 | self.shouldFail = YES;
32 | self.error = error;
33 | }
34 | return self;
35 | }
36 |
37 | -(id)initWithStatusCode:(NSInteger)statusCode {
38 | self = [super init];
39 | if (self) {
40 | self.shouldFail = NO;
41 | self.statusCode = statusCode;
42 | self.mutableHeaders = [NSMutableDictionary dictionary];
43 | self.body = [@"" dataUsingEncoding:NSUTF8StringEncoding];
44 | }
45 | return self;
46 | }
47 |
48 | - (id)initWithRawResponse:(NSData *)rawResponseData {
49 | self = [self initDefaultResponse];
50 | if (self) {
51 | CFHTTPMessageRef httpMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, FALSE);
52 | if (httpMessage) {
53 | CFHTTPMessageAppendBytes(httpMessage, [rawResponseData bytes], [rawResponseData length]);
54 |
55 | self.body = rawResponseData; // By default
56 |
57 | if (CFHTTPMessageIsHeaderComplete(httpMessage)) {
58 | self.statusCode = (NSInteger)CFHTTPMessageGetResponseStatusCode(httpMessage);
59 | self.mutableHeaders = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)CFHTTPMessageCopyAllHeaderFields(httpMessage)];
60 | self.body = (__bridge_transfer NSData *)CFHTTPMessageCopyBody(httpMessage);
61 | }
62 | CFRelease(httpMessage);
63 | }
64 | }
65 | return self;
66 | }
67 |
68 | - (void)setHeader:(NSString *)header value:(NSString *)value {
69 | [self.mutableHeaders setValue:value forKey:header];
70 | }
71 | - (NSDictionary *)headers {
72 | return [NSDictionary dictionaryWithDictionary:self.mutableHeaders];
73 | }
74 |
75 | - (NSString *)description {
76 | return [NSString stringWithFormat:@"StubRequest:\nStatus Code: %ld\nHeaders: %@\nBody: %@",
77 | (long)self.statusCode,
78 | self.mutableHeaders,
79 | self.body];
80 | }
81 | @end
82 |
--------------------------------------------------------------------------------
/NocillaTests/DSL/LSHTTPRequestDSLRepresentationSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSTestRequest.h"
3 | #import "LSHTTPRequestDSLRepresentation.h"
4 |
5 | SPEC_BEGIN(LSHTTPRequestDSLRepresentationSpec)
6 | describe(@"description", ^{
7 | __block LSHTTPRequestDSLRepresentation *dsl = nil;
8 | describe(@"a request with a GET method and url", ^{
9 | beforeEach(^{
10 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
11 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
12 | });
13 | it(@"should return the DSL representation", ^{
14 | [[[dsl description] should] equal:@"stubRequest(@\"GET\", @\"http://www.google.com\");"];
15 | });
16 | });
17 | describe(@"a request with a POST method and a url", ^{
18 | beforeEach(^{
19 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
20 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
21 |
22 | });
23 | it(@"should return the DSL representation", ^{
24 | [[[dsl description] should] equal:@"stubRequest(@\"POST\", @\"http://luissolano.com\");"];
25 | });
26 | });
27 | describe(@"a request with one header", ^{
28 | beforeEach(^{
29 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
30 | [request setHeader:@"Accept" value:@"text/plain"];
31 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
32 | });
33 | it(@"should return the DSL representation", ^{
34 | [[[dsl description] should] equal:@"stubRequest(@\"POST\", @\"http://luissolano.com\").\nwithHeaders(@{ @\"Accept\": @\"text/plain\" });"];
35 | });
36 | });
37 | describe(@"a request with 3 headers", ^{
38 | beforeEach(^{
39 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
40 | [request setHeader:@"Accept" value:@"text/plain"];
41 | [request setHeader:@"Content-Length" value:@"18"];
42 | [request setHeader:@"If-Match" value:@"a8fhw0dhasd03qn02"];
43 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
44 |
45 | });
46 | it(@"should return the DSL representation", ^{
47 | NSString *expected = @"stubRequest(@\"POST\", @\"http://luissolano.com\").\nwithHeaders(@{ @\"Accept\": @\"text/plain\", @\"Content-Length\": @\"18\", @\"If-Match\": @\"a8fhw0dhasd03qn02\" });";
48 | [[[dsl description] should] equal:expected];
49 | });
50 | });
51 | describe(@"when a header contains a double quoute", ^{
52 | beforeEach(^{
53 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
54 | [request setHeader:@"X-MY-HEADER" value:@"quote\"quoute"];
55 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
56 | });
57 | it(@"should escape the result", ^{
58 | [[[dsl description] should] equal:@"stubRequest(@\"POST\", @\"http://luissolano.com\").\nwithHeaders(@{ @\"X-MY-HEADER\": @\"quote\"quoute\" });"];
59 | });
60 | });
61 | describe(@"a request with headers and body", ^{
62 | beforeEach(^{
63 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
64 | [request setHeader:@"Accept" value:@"text/plain"];
65 | [request setHeader:@"Content-Length" value:@"18"];
66 | [request setHeader:@"If-Match" value:@"a8fhw0dhasd03qn02"];
67 | [request setBody:[@"The body of a request, yeah!" dataUsingEncoding:NSUTF8StringEncoding]];
68 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
69 |
70 | });
71 | it(@"should return the DSL representation", ^{
72 | NSString *expected = @"stubRequest(@\"POST\", @\"http://luissolano.com\").\nwithHeaders(@{ @\"Accept\": @\"text/plain\", @\"Content-Length\": @\"18\", @\"If-Match\": @\"a8fhw0dhasd03qn02\" }).\nwithBody(@\"The body of a request, yeah!\");";
73 | [[[dsl description] should] equal:expected];
74 | });
75 | });
76 | context(@"when the body contain double quotes", ^{
77 | beforeEach(^{
78 | LSTestRequest *request = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://luissolano.com"];
79 | [request setBody:[@"{\"text\":\"adios\"}" dataUsingEncoding:NSUTF8StringEncoding]];
80 | dsl = [[LSHTTPRequestDSLRepresentation alloc] initWithRequest:request];
81 | });
82 | it(@"should return the DSL representation", ^{
83 | NSString *expected = @"stubRequest(@\"POST\", @\"http://luissolano.com\").\nwithBody(@\"{\\\"text\\\":\\\"adios\\\"}\");";
84 | [[[dsl description] should] equal:expected];
85 | });
86 | });
87 | });
88 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Diff/LSHTTPRequestDiffSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSHTTPRequest.h"
3 | #import "LSHTTPRequestDiff.h"
4 | #import "LSTestRequest.h"
5 |
6 | SPEC_BEGIN(LSHTTPRequestDiffSpec)
7 | describe(@"diffing two LSHTTPRequests", ^{
8 | __block LSTestRequest *oneRequest = nil;
9 | __block LSTestRequest *anotherRequest = nil;
10 | __block LSHTTPRequestDiff *diff = nil;
11 | context(@"when both represent the same request", ^{
12 | beforeEach(^{
13 | NSString *urlString = @"http://www.google.com";
14 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:urlString];
15 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:urlString];
16 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
17 | });
18 |
19 | it(@"should result in an empty diff", ^{
20 | [[theValue(diff.isEmpty) should] beYes];
21 | });
22 | it(@"should an empty description", ^{
23 | [[[diff description] should] equal:@""];
24 | });
25 | });
26 | context(@"when the request differ in the method", ^{
27 | beforeEach(^{
28 | NSString *urlString = @"http://www.google.com";
29 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:urlString];
30 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"POST" url:urlString];
31 | });
32 | it(@"should not be empty", ^{
33 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
34 | [[theValue(diff.isEmpty) should] beNo];
35 | });
36 | context(@"in one direction", ^{
37 | beforeEach(^{
38 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
39 | });
40 | it(@"should have a description representing the diff", ^{
41 | NSString *expected = @"- Method: GET\n+ Method: POST\n";
42 | [[[diff description] should] equal:expected];
43 | });
44 |
45 | });
46 | context(@"in the other direction", ^{
47 | beforeEach(^{
48 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
49 | });
50 | it(@"should have a description representing the diff", ^{
51 | NSString *expected = @"- Method: POST\n+ Method: GET\n";
52 | [[[diff description] should] equal:expected];
53 | });
54 |
55 | });
56 | });
57 |
58 | context(@"when the requests differ in the URL", ^{
59 | beforeEach(^{
60 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
61 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.luissolano.com"];
62 | });
63 | it(@"should not be empty", ^{
64 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
65 | [[theValue(diff.isEmpty) should] beNo];
66 | });
67 | context(@"in one direction", ^{
68 | beforeEach(^{
69 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
70 | });
71 | it(@"should have a description representing the diff", ^{
72 | NSString *expected = @"- URL: http://www.google.com\n+ URL: http://www.luissolano.com\n";
73 | [[[diff description] should] equal:expected];
74 | });
75 | });
76 | context(@"in the other direction", ^{
77 | beforeEach(^{
78 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
79 | });
80 | it(@"should have a description representing the diff", ^{
81 | NSString *expected = @"- URL: http://www.luissolano.com\n+ URL: http://www.google.com\n";
82 | [[[diff description] should] equal:expected];
83 | });
84 | });
85 | });
86 | context(@"when the request differ in one header", ^{
87 | beforeEach(^{
88 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
89 | [oneRequest setHeader:@"Content-Type" value:@"application/json"];
90 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
91 | });
92 | it(@"should not be empty", ^{
93 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
94 | [[theValue(diff.isEmpty) should] beNo];
95 | });
96 | context(@"in one direction", ^{
97 | beforeEach(^{
98 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
99 | });
100 | it(@"should have a description representing the diff", ^{
101 | NSString *expected = @" Headers:\n-\t\"Content-Type\": \"application/json\"\n";
102 | [[[diff description] should] equal:expected];
103 | });
104 |
105 | });
106 | context(@"in the other direction", ^{
107 | beforeEach(^{
108 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
109 | });
110 | it(@"should have a description representing the diff", ^{
111 | NSString *expected = @" Headers:\n+\t\"Content-Type\": \"application/json\"\n";
112 | [[[diff description] should] equal:expected];
113 | });
114 |
115 | });
116 |
117 | });
118 |
119 | context(@"when the request differ in one header each", ^{
120 | beforeEach(^{
121 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
122 | [oneRequest setHeader:@"Content-Type" value:@"application/json"];
123 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
124 | [anotherRequest setHeader:@"Accept" value:@"text/plain"];
125 | });
126 | it(@"should not be empty", ^{
127 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
128 | [[theValue(diff.isEmpty) should] beNo];
129 | });
130 | context(@"in one direction", ^{
131 | beforeEach(^{
132 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
133 | });
134 | it(@"should have a description representing the diff", ^{
135 | NSString *expected = @" Headers:\n-\t\"Content-Type\": \"application/json\"\n+\t\"Accept\": \"text/plain\"\n";
136 | [[[diff description] should] equal:expected];
137 | });
138 | });
139 | context(@"in the other direction", ^{
140 | beforeEach(^{
141 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
142 | });
143 | it(@"should have a description representing the diff", ^{
144 | NSString *expected = @" Headers:\n-\t\"Accept\": \"text/plain\"\n+\t\"Content-Type\": \"application/json\"\n";
145 | [[[diff description] should] equal:expected];
146 | });
147 |
148 | });
149 | });
150 | context(@"when the requests differ in the body", ^{
151 | beforeEach(^{
152 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
153 | oneRequest.body = [@"this is a body" dataUsingEncoding:NSUTF8StringEncoding];
154 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
155 | });
156 | it(@"should not be empty", ^{
157 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
158 | [[theValue(diff.isEmpty) should] beNo];
159 | });
160 | context(@"in one direction", ^{
161 | beforeEach(^{
162 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
163 | });
164 | it(@"should have a description representing the diff", ^{
165 | NSString *expected = @"- Body: \"this is a body\"\n";
166 | [[[diff description] should] equal:expected];
167 | });
168 | });
169 | context(@"in the other direction", ^{
170 | beforeEach(^{
171 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
172 | });
173 | it(@"should have a description representing the diff", ^{
174 | NSString *expected = @"+ Body: \"this is a body\"\n";
175 |
176 | [[[diff description] should] equal:expected];
177 | });
178 |
179 | });
180 | });
181 | context(@"when the requests differ in the Method and the URL", ^{
182 | beforeEach(^{
183 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
184 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"http://www.luissolano.com"];
185 | });
186 | context(@"in one direction", ^{
187 | beforeEach(^{
188 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
189 | });
190 | it(@"should have a description representing the diff", ^{
191 | NSString *expected = @"- Method: GET\n+ Method: PUT\n- URL: http://www.google.com\n+ URL: http://www.luissolano.com\n";
192 | [[[diff description] should] equal:expected];
193 | });
194 | });
195 | context(@"in the other direction", ^{
196 | beforeEach(^{
197 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
198 | });
199 | it(@"should have a description representing the diff", ^{
200 | NSString *expected = @"- Method: PUT\n+ Method: GET\n- URL: http://www.luissolano.com\n+ URL: http://www.google.com\n";
201 | [[[diff description] should] equal:expected];
202 | });
203 |
204 | });
205 | });
206 | context(@"when the request differ in the Method, URL and headers", ^{
207 | beforeEach(^{
208 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"POST" url:@"http://www.google.es"];
209 | [oneRequest setHeader:@"X-API-TOKEN" value:@"123456789"];
210 | [oneRequest setHeader:@"Accept" value:@"application/json"];
211 | [oneRequest setHeader:@"X-Custom-Header" value:@"Really??"];
212 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"DELETE" url:@"http://www.luissolano.com/ispellchecker/"];
213 | [anotherRequest setHeader:@"Accept" value:@"application/json"];
214 | [anotherRequest setHeader:@"X-API-TOKEN" value:@"abcedfghi"];
215 | [anotherRequest setHeader:@"X-APP-ID" value:@"Nocilla"];
216 | });
217 | context(@"in one direction", ^{
218 | beforeEach(^{
219 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
220 | });
221 | it(@"should have a description representing the diff", ^{
222 | NSString *expected = @"- Method: POST\n+ Method: DELETE\n- URL: http://www.google.es\n+ URL: http://www.luissolano.com/ispellchecker/\n Headers:\n-\t\"X-API-TOKEN\": \"123456789\"\n-\t\"X-Custom-Header\": \"Really??\"\n+\t\"X-API-TOKEN\": \"abcedfghi\"\n+\t\"X-APP-ID\": \"Nocilla\"\n";
223 | [[[diff description] should] equal:expected];
224 | });
225 | });
226 | context(@"in the other direction", ^{
227 | beforeEach(^{
228 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
229 | });
230 | it(@"should have a description representing the diff", ^{
231 | NSString *expected = @"- Method: DELETE\n+ Method: POST\n- URL: http://www.luissolano.com/ispellchecker/\n+ URL: http://www.google.es\n Headers:\n-\t\"X-API-TOKEN\": \"abcedfghi\"\n-\t\"X-APP-ID\": \"Nocilla\"\n+\t\"X-API-TOKEN\": \"123456789\"\n+\t\"X-Custom-Header\": \"Really??\"\n";
232 | [[[diff description] should] equal:expected];
233 | });
234 | });
235 | });
236 | context(@"when the requests differ in everything", ^{
237 | beforeEach(^{
238 | oneRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://www.google.it"];
239 | [oneRequest setHeader:@"X-API-TOKEN" value:@"123456789"];
240 | [oneRequest setHeader:@"Accept" value:@"application/json"];
241 | [oneRequest setHeader:@"X-Custom-Header" value:@"Really??"];
242 | [oneRequest setBody:[@"This is a body" dataUsingEncoding:NSUTF8StringEncoding]];
243 | anotherRequest = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"http://www.luissolano.com"];
244 | [anotherRequest setHeader:@"X-API-TOKEN" value:@"123456789"];
245 | [anotherRequest setHeader:@"X-APP-ID" value:@"Nocilla"];
246 | [anotherRequest setBody:[@"This is THE body" dataUsingEncoding:NSUTF8StringEncoding]];
247 | });
248 | context(@"in one direction", ^{
249 | beforeEach(^{
250 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:oneRequest andRequest:anotherRequest];
251 | });
252 | it(@"should have a description representing the diff", ^{
253 | NSString *expected = @"- Method: PUT\n+ Method: GET\n- URL: https://www.google.it\n+ URL: http://www.luissolano.com\n Headers:\n-\t\"Accept\": \"application/json\"\n-\t\"X-Custom-Header\": \"Really??\"\n+\t\"X-APP-ID\": \"Nocilla\"\n- Body: \"This is a body\"\n+ Body: \"This is THE body\"\n";
254 | [[[diff description] should] equal:expected];
255 | });
256 | });
257 | context(@"in the other direction", ^{
258 | beforeEach(^{
259 | diff = [[LSHTTPRequestDiff alloc] initWithRequest:anotherRequest andRequest:oneRequest];
260 | });
261 | it(@"should have a description representing the diff", ^{
262 | NSString *expected = @"- Method: GET\n+ Method: PUT\n- URL: http://www.luissolano.com\n+ URL: https://www.google.it\n Headers:\n-\t\"X-APP-ID\": \"Nocilla\"\n+\t\"Accept\": \"application/json\"\n+\t\"X-Custom-Header\": \"Really??\"\n- Body: \"This is THE body\"\n+ Body: \"This is a body\"\n";
263 | [[[diff description] should] equal:expected];
264 | });
265 | });
266 | });
267 | });
268 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/ASIHTTPRequest/ASIHTTPRequestStubbingSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "Nocilla.h"
3 | #import "ASIHTTPRequest.h"
4 | #import "ASIFormDataRequest.h"
5 | #import "LSASIHTTPRequestHook.h"
6 |
7 | SPEC_BEGIN(ASIHTTPRequestStubbingSpec)
8 |
9 | beforeEach(^{
10 | [[LSNocilla sharedInstance] start];
11 | });
12 |
13 | afterEach(^{
14 | [[LSNocilla sharedInstance] stop];
15 | [[LSNocilla sharedInstance] clearStubs];
16 | });
17 |
18 | it(@"stubs a GET request", ^{
19 | stubRequest(@"GET", @"http://httpstat.us/400").
20 | andReturn(201).
21 | withHeaders(@{@"Header 1":@"Foo", @"Header 2":@"Bar"}).
22 | withBody(@"Holaa!");
23 |
24 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://httpstat.us/400"]];
25 |
26 | [request startAsynchronous];
27 |
28 | [request waitUntilFinished];
29 |
30 | [[expectFutureValue(theValue(request.responseStatusCode)) shouldEventually] equal:theValue(201)];
31 | [[request.responseString should] equal:@"Holaa!"];
32 | [[request.responseData should] equal:[@"Holaa!" dataUsingEncoding:NSUTF8StringEncoding]];
33 | [[request.responseHeaders should] equal:@{@"Header 1":@"Foo", @"Header 2":@"Bar"}];
34 | [[theValue(request.isFinished) should] beYes];
35 |
36 | });
37 |
38 | it(@"stubs a POST request", ^{
39 | stubRequest(@"POST", @"http://api.example.com/v1/dogs.json").
40 | withHeaders(@{@"Authorization":@"Bearer 123123123"}).
41 | withBody(@"{\"dog\":\"pepu\"}").
42 | andReturn(201).
43 | withHeaders(@{@"Header 1":@"Foo", @"Header 2":@"Bar"}).
44 | withBody(@"Holaa!");
45 |
46 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://api.example.com/v1/dogs.json"]];
47 | request.requestMethod = @"POST";
48 | [request addRequestHeader:@"Authorization" value:@"Bearer 123123123"];
49 | [request appendPostData:[@"{\"dog\":\"pepu\"}" dataUsingEncoding:NSUTF8StringEncoding]];
50 |
51 | [request startAsynchronous];
52 |
53 | [request waitUntilFinished];
54 |
55 | [[expectFutureValue(theValue(request.responseStatusCode)) shouldEventually] equal:theValue(201)];
56 | [[request.responseString should] equal:@"Holaa!"];
57 | [[request.responseData should] equal:[@"Holaa!" dataUsingEncoding:NSUTF8StringEncoding]];
58 | [[request.responseHeaders should] equal:@{@"Header 1":@"Foo", @"Header 2":@"Bar"}];
59 | [[theValue(request.isFinished) should] beYes];
60 | });
61 |
62 | it(@"fails a request", ^{
63 | NSError *error = [NSError nullMock];
64 | stubRequest(@"POST", @"http://api.example.com/v1/cats").
65 | withHeaders(@{@"Authorization":@"Basic 667788"}).
66 | withBody(@"name=calcetines&color=black").
67 | andFailWithError(error);
68 |
69 | ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://api.example.com/v1/cats"]];
70 | [request setRequestMethod:@"POST"];
71 | [request addRequestHeader:@"Authorization" value:@"Basic 667788"];
72 | [request addPostValue:@"calcetines" forKey:@"name"];
73 | [request addPostValue:@"black" forKey:@"color"];
74 |
75 | [request startAsynchronous];
76 |
77 | [request waitUntilFinished];
78 |
79 | [[expectFutureValue(request.error) shouldEventually] beIdenticalTo:error];
80 | [[theValue(request.isFinished) should] beYes];
81 | });
82 |
83 | it(@"stubs an HTTPS request", ^{
84 | stubRequest(@"GET", @"https://example.com/things").
85 | andReturn(201).
86 | withHeaders(@{@"Header 1":@"Foo", @"Header 2":@"Bar"}).
87 | withBody(@"Holaa!");
88 |
89 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://example.com/things"]];
90 |
91 | [request startAsynchronous];
92 |
93 | [request waitUntilFinished];
94 |
95 | [[expectFutureValue(theValue(request.responseStatusCode)) shouldEventually] equal:theValue(201)];
96 | [[request.responseString should] equal:@"Holaa!"];
97 | [[request.responseData should] equal:[@"Holaa!" dataUsingEncoding:NSUTF8StringEncoding]];
98 | [[request.responseHeaders should] equal:@{@"Header 1":@"Foo", @"Header 2":@"Bar"}];
99 | [[theValue(request.isFinished) should] beYes];
100 | });
101 |
102 | it(@"stubs a ASIFormDataRequest", ^{
103 | stubRequest(@"POST", @"http://api.example.com/v1/cats").
104 | withHeaders(@{@"Authorization":@"Basic 667788"}).
105 | withBody(@"name=calcetines&color=black").
106 | andReturn(201).
107 | withHeaders(@{@"Header 1":@"Foo", @"Header 2":@"Bar"}).
108 | withBody(@"Holaa!");
109 |
110 | ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://api.example.com/v1/cats"]];
111 | [request setRequestMethod:@"POST"];
112 | [request addRequestHeader:@"Authorization" value:@"Basic 667788"];
113 | [request addPostValue:@"calcetines" forKey:@"name"];
114 | [request addPostValue:@"black" forKey:@"color"];
115 |
116 |
117 | [request startAsynchronous];
118 |
119 | [request waitUntilFinished];
120 |
121 | [[expectFutureValue(theValue(request.responseStatusCode)) shouldEventually] equal:theValue(201)];
122 | [[request.responseString should] equal:@"Holaa!"];
123 | [[request.responseData should] equal:[@"Holaa!" dataUsingEncoding:NSUTF8StringEncoding]];
124 | [[request.responseHeaders should] equal:@{@"Header 1":@"Foo", @"Header 2":@"Bar"}];
125 | [[theValue(request.isFinished) should] beYes];
126 | });
127 |
128 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/ASIHTTPRequest/LSASIHTTPRequestHookSpec.m:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luisobo/Nocilla/3a7fabeade4cd3329d3988728eee0416e6c689de/NocillaTests/Hooks/ASIHTTPRequest/LSASIHTTPRequestHookSpec.m
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLRequest/AFNetworkingStubbingSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "AFNetworking.h"
3 | #import "Nocilla.h"
4 | #import "HTTPServer.h"
5 | #import "LSTestingConnection.h"
6 |
7 | SPEC_BEGIN(AFNetworkingStubbingSpec)
8 |
9 | beforeEach(^{
10 | [[LSNocilla sharedInstance] start];
11 | });
12 | afterEach(^{
13 | [[LSNocilla sharedInstance] stop];
14 | [[LSNocilla sharedInstance] clearStubs];
15 | });
16 |
17 | context(@"AFNetworking", ^{
18 | it(@"should stub the request", ^{
19 | stubRequest(@"POST", @"https://example.com/say-hello").
20 | withHeader(@"Content-Type", @"text/plain").
21 | withHeader(@"X-MY-AWESOME-HEADER", @"sisisi").
22 | withBody(@"Adios!").
23 | andReturn(200).
24 | withHeader(@"Content-Type", @"text/plain").
25 | withBody(@"hola");
26 |
27 | NSURL *url = [NSURL URLWithString:@"https://example.com/say-hello"];
28 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
29 | [request setHTTPMethod:@"POST"];
30 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
31 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
32 | [request setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
33 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
34 | [operation start];
35 |
36 | [operation waitUntilFinished];
37 |
38 | [operation.error shouldBeNil];
39 | [[operation.responseString should] equal:@"hola"];
40 | [[theValue(operation.response.statusCode) should] equal:theValue(200)];
41 | [[[operation.response.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"text/plain"];
42 | });
43 |
44 | it(@"should stub the request with a raw reponse", ^{
45 | stubRequest(@"POST", @"https://example.com/say-hello").
46 | withHeader(@"Content-Type", @"text/plain").
47 | withHeader(@"X-MY-AWESOME-HEADER", @"sisisi").
48 | withBody(@"Adios!").
49 | andReturnRawResponse([@"HTTP/1.1 200 OK\nContent-Type: text/plain\n\nhola" dataUsingEncoding:NSUTF8StringEncoding]);
50 |
51 | NSURL *url = [NSURL URLWithString:@"https://example.com/say-hello"];
52 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
53 | [request setHTTPMethod:@"POST"];
54 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
55 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
56 | [request setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
57 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
58 | [operation start];
59 |
60 | [operation waitUntilFinished];
61 |
62 | [operation.error shouldBeNil];
63 | [[operation.responseString should] equal:@"hola"];
64 | [[theValue(operation.response.statusCode) should] equal:theValue(200)];
65 | [[[operation.response.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"text/plain"];
66 | });
67 |
68 | it(@"should have the same result as a real HTTP request", ^{
69 | NSURL *url = [NSURL URLWithString:@"http://localhost:12345/say-hello"];
70 |
71 | [[LSNocilla sharedInstance] stop];
72 |
73 | HTTPServer *server = [[HTTPServer alloc] init];
74 | [server setPort:12345];
75 | NSError *error;
76 | [server setConnectionClass:[LSTestingConnection class]];
77 | [server start:&error];
78 | [error shouldBeNil];
79 |
80 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
81 | [request setHTTPMethod:@"POST"];
82 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
83 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
84 | [request setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
85 | AFHTTPRequestOperation *realOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
86 | [realOperation start];
87 |
88 | [realOperation waitUntilFinished];
89 |
90 | [realOperation.error shouldBeNil];
91 | [[realOperation.responseString should] equal:@"hola"];
92 | [[theValue(realOperation.response.statusCode) should] equal:theValue(200)];
93 |
94 | NSHTTPURLResponse *realResponse = realOperation.response;
95 | NSString *realBody = realOperation.responseString;
96 |
97 | [server stop];
98 |
99 | [[LSNocilla sharedInstance] start];
100 |
101 | stubRequest(@"POST", @"http://localhost:12345/say-hello").
102 | withHeaders(@{@"Content-Type" : @"text/plain", @"X-MY-AWESOME-HEADER" : @"sisisi"}).
103 | withBody(@"Adios!").
104 | andReturn(200).
105 | withHeaders([realResponse allHeaderFields]).
106 | withBody(@"hola");
107 |
108 |
109 |
110 | NSMutableURLRequest *stubbedRequest = [NSMutableURLRequest requestWithURL:url];
111 | [stubbedRequest setHTTPMethod:@"POST"];
112 | [stubbedRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
113 | [stubbedRequest setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
114 | [stubbedRequest setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
115 | AFHTTPRequestOperation *stubbedOperation = [[AFHTTPRequestOperation alloc] initWithRequest:stubbedRequest];
116 | [stubbedOperation start];
117 |
118 | [stubbedOperation waitUntilFinished];
119 |
120 | [stubbedOperation.error shouldBeNil];
121 | [[stubbedOperation.responseString should] equal:@"hola"];
122 | [[theValue(stubbedOperation.response.statusCode) should] equal:theValue(200)];
123 |
124 | NSHTTPURLResponse *stubbedResponse = stubbedOperation.response;
125 | NSString *stubbedBody = stubbedOperation.responseString;
126 |
127 |
128 | NSDictionary *realHeaders = [realResponse allHeaderFields];
129 | NSDictionary *stubbedHeaders = [stubbedResponse allHeaderFields];
130 | [[theValue(realResponse.statusCode) should] equal:theValue(stubbedResponse.statusCode)];
131 | [[realHeaders should] equal:stubbedHeaders];
132 | [[realBody should] equal:stubbedBody];
133 | });
134 |
135 | it(@"should stub an asynchronous request", ^{
136 | stubRequest(@"POST", @"https://example.com/say-hello").
137 | withHeaders(@{ @"X-MY-AWESOME-HEADER": @"sisisi", @"Content-Type": @"text/plain" }).
138 | withBody(@"Adios!").
139 | andReturn(200).
140 | withHeader(@"Content-Type", @"text/plain").
141 | withBody(@"hola");
142 |
143 | NSURL *url = [NSURL URLWithString:@"https://example.com/say-hello"];
144 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
145 | [request setHTTPMethod:@"POST"];
146 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
147 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
148 | [request setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
149 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
150 | [operation start];
151 |
152 | [[expectFutureValue(theValue(operation.response.statusCode)) shouldEventually] equal:theValue(200)];
153 | [[operation.responseString should] equal:@"hola"];
154 |
155 | [[[operation.response.allHeaderFields objectForKey:@"Content-Type"] shouldEventually] equal:@"text/plain"];
156 | [operation.error shouldBeNil];
157 | });
158 |
159 | it(@"stubs a request with an error", ^{
160 | NSError *error = [NSError errorWithDomain:@"com.luisobo.nocilla" code:123 userInfo:@{NSLocalizedDescriptionKey:@"Failing, failing... 1, 2, 3..."}];
161 |
162 | stubRequest(@"POST", @"https://example.com/say-hello").
163 | withHeaders(@{ @"X-MY-AWESOME-HEADER": @"sisisi", @"Content-Type": @"text/plain" }).
164 | withBody(@"Adios!").
165 | andFailWithError(error);
166 |
167 | NSURL *url = [NSURL URLWithString:@"https://example.com/say-hello"];
168 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
169 | [request setHTTPMethod:@"POST"];
170 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
171 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
172 | [request setHTTPBody:[@"Adios!" dataUsingEncoding:NSASCIIStringEncoding]];
173 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
174 |
175 |
176 | __block BOOL succeed = NO;
177 | __block BOOL failed = NO;
178 | __block NSError *capturedError = nil;
179 | [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
180 | succeed = YES;
181 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
182 | capturedError = error;
183 | failed = YES;
184 | }];
185 |
186 | [operation start];
187 |
188 | [[expectFutureValue(theValue(failed)) shouldEventually] beYes];
189 | [[capturedError should] equal:[NSError errorWithDomain:@"com.luisobo.nocilla" code:123 userInfo:@{NSLocalizedDescriptionKey:@"Failing, failing... 1, 2, 3...",
190 | @"NSErrorFailingURLKey":[NSURL URLWithString:@"https://example.com/say-hello"],
191 | @"NSErrorFailingURLStringKey":@"https://example.com/say-hello"
192 | }]];
193 |
194 | [[operation.error should] equal:[NSError errorWithDomain:@"com.luisobo.nocilla" code:123 userInfo:@{NSLocalizedDescriptionKey:@"Failing, failing... 1, 2, 3...",
195 | @"NSErrorFailingURLKey":[NSURL URLWithString:@"https://example.com/say-hello"],
196 | @"NSErrorFailingURLStringKey":@"https://example.com/say-hello"
197 | }]];
198 | });
199 |
200 | it(@"stubs the request with data", ^{
201 | NSData *requestData = [@"data123" dataUsingEncoding:NSUTF8StringEncoding];
202 | stubRequest(@"POST", @"https://example.com/say-hello").
203 | withHeader(@"Content-Type", @"text/plain").
204 | withHeader(@"X-MY-AWESOME-HEADER", @"sisisi").
205 | withBody(requestData).
206 | andReturn(200).
207 | withHeader(@"Content-Type", @"text/plain").
208 | withBody([@"eeeeooo" dataUsingEncoding:NSUTF8StringEncoding]);
209 |
210 | NSURL *url = [NSURL URLWithString:@"https://example.com/say-hello"];
211 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
212 | [request setHTTPMethod:@"POST"];
213 | [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
214 | [request setValue:@"sisisi" forHTTPHeaderField:@"X-MY-AWESOME-HEADER"];
215 | [request setHTTPBody:requestData];
216 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
217 | [operation start];
218 |
219 | [operation waitUntilFinished];
220 |
221 | [operation.error shouldBeNil];
222 | [[operation.responseString should] equal:@"eeeeooo"];
223 | [[theValue(operation.response.statusCode) should] equal:theValue(200)];
224 | [[[operation.response.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"text/plain"];
225 |
226 | });
227 |
228 | it(@"stubs the request with multipart request", ^{
229 | __block BOOL done = NO;
230 | __block AFHTTPRequestOperation *capturedOperation;
231 | __block id capturedResponseObject;
232 |
233 | NSData *requestData = [@"data123" dataUsingEncoding:NSUTF8StringEncoding];
234 | stubRequest(@"POST", @"https://example.com/say-hello").
235 | andReturn(200).
236 | withHeader(@"Content-Type", @"application/json").
237 | withBody(@"{\"foo\":\"bar\"}");
238 |
239 | NSURL *url = [NSURL URLWithString:@"https://example.com"];
240 | AFHTTPRequestOperationManager * client = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:url];
241 | NSInputStream * stream = [[NSInputStream alloc] initWithData:requestData];
242 |
243 | [client POST:@"say-hello" parameters:nil constructingBodyWithBlock:^(id formData) {
244 | [formData appendPartWithInputStream:stream name:@"name" fileName:@"filename" length:10 mimeType:@"application/octet-stream"];
245 | } success:^(AFHTTPRequestOperation *operation, id responseObject) {
246 | capturedOperation = operation;
247 | capturedResponseObject = responseObject;
248 | done = YES;
249 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
250 | done = YES;
251 | }];
252 |
253 | [[expectFutureValue(theValue(done)) shouldEventually] beYes];
254 |
255 | [capturedOperation.error shouldBeNil];
256 | [[capturedOperation.responseString should] equal:@"{\"foo\":\"bar\"}"];
257 | [[theValue(capturedOperation.response.statusCode) should] equal:theValue(200)];
258 | [[[capturedOperation.response.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"application/json"];
259 | [[capturedResponseObject should] equal:@{@"foo": @"bar"}];
260 | });
261 |
262 | it(@"should properly handle redirects", ^{
263 | stubRequest(@"GET", @"https://example.com/hello").
264 | andReturn(301).
265 | withHeader(@"Location", @"https://example.com/hola");
266 |
267 | stubRequest(@"GET", @"https://example.com/hola").
268 | andReturn(200).
269 | withHeader(@"Content-Type", @"text/plain").
270 | withBody(@"hola");
271 |
272 | NSURL *url = [NSURL URLWithString:@"https://example.com/hello"];
273 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
274 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
275 | [operation start];
276 |
277 | [operation waitUntilFinished];
278 |
279 | [operation.error shouldBeNil];
280 | [[operation.responseString should] equal:@"hola"];
281 | [[theValue(operation.response.statusCode) should] equal:theValue(200)];
282 | [[[operation.response.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"text/plain"];
283 | });
284 |
285 | it(@"should properly sets cookies on a non-redirected request", ^{
286 | stubRequest(@"GET", @"https://example.com/hello").
287 | andReturn(200).
288 | withBody(@"hola").
289 | withHeader(@"Content-Type", @"text/plain").
290 | withHeader(@"Set-Cookie", @"giveme=cookies;Path=/;");
291 |
292 | NSURL *url = [NSURL URLWithString:@"https://example.com/hello"];
293 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
294 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
295 | [operation start];
296 |
297 | [operation waitUntilFinished];
298 |
299 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
300 | NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:@"https://example.com/"]];
301 | [[cookies should] haveCountOf:1];
302 | NSHTTPCookie *cookie = cookies[0];
303 | [[[cookie name] should] equal:@"giveme"];
304 | [[[cookie value] should] equal:@"cookies"];
305 | });
306 |
307 | it(@"should properly sets cookies on redirect", ^{
308 | stubRequest(@"GET", @"https://example.com/hello").
309 | andReturn(301).
310 | withHeaders(@{@"Location": @"https://example.com/hola",
311 | @"Set-Cookie": @"giveme=cookies;Path=/;"});
312 |
313 | stubRequest(@"GET", @"https://example.com/hola").
314 | withHeader(@"Cookie", @"giveme=cookies").
315 | andReturn(200).
316 | withHeader(@"Content-Type", @"text/plain").
317 | withBody(@"hola");
318 |
319 | NSURL *url = [NSURL URLWithString:@"https://example.com/hello"];
320 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
321 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
322 | [operation start];
323 |
324 | [operation waitUntilFinished];
325 |
326 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
327 | NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:@"https://example.com/"]];
328 | [[cookies should] haveCountOf:1];
329 | NSHTTPCookie *cookie = cookies[0];
330 | [[[cookie name] should] equal:@"giveme"];
331 | [[[cookie value] should] equal:@"cookies"];
332 |
333 | });
334 | });
335 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLRequest/LSHTTPStubURLProtocolSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSHTTPStubURLProtocol.h"
3 | #import "LSStubRequest.h"
4 | #import "LSStubResponse.h"
5 | #import "LSNocilla.h"
6 |
7 | @interface NSHTTPURLResponse(UndocumentedInitializer)
8 | - (id)initWithURL:(NSURL*)URL statusCode:(NSInteger)statusCode headerFields:(NSDictionary*)headerFields requestTime:(double)requestTime;
9 | @end
10 |
11 | @interface LSTestingNSURLProtocolClient : NSObject
12 | @property (nonatomic, strong) NSURLResponse *response;
13 | @property (nonatomic, strong) NSData *body;
14 | @end
15 |
16 | @implementation LSTestingNSURLProtocolClient
17 | - (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse {
18 |
19 | }
20 | - (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse {
21 |
22 | }
23 | - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy {
24 | self.response = response;
25 | }
26 | - (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data {
27 | self.body = data;
28 |
29 | }
30 | - (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol {
31 |
32 | }
33 | - (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error {
34 |
35 | }
36 | - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
37 |
38 | }
39 | - (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
40 |
41 | }
42 |
43 |
44 | @end
45 | SPEC_BEGIN(LSHTTPStubURLProtocolSpec)
46 |
47 | describe(@"+canInitWithRequest", ^{
48 | context(@"when it is a HTTP request", ^{
49 | it(@"should return YES", ^{
50 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://api.example.com/dogs.json"]];
51 |
52 | BOOL result = [LSHTTPStubURLProtocol canInitWithRequest:request];
53 |
54 | [[theValue(result) should] beYes];
55 | });
56 | });
57 | context(@"when it is a HTTP request", ^{
58 | it(@"should return YES", ^{
59 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://api.example.com/cats.xml"]];
60 |
61 | BOOL result = [LSHTTPStubURLProtocol canInitWithRequest:request];
62 |
63 | [[theValue(result) should] beYes];
64 | });
65 | });
66 |
67 | context(@"when it is an FTP request", ^{
68 | it(@"should return NO", ^{
69 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"ftp://ftp.example.com/"]];
70 |
71 | BOOL result = [LSHTTPStubURLProtocol canInitWithRequest:request];
72 |
73 | [[theValue(result) should] beNo];
74 | });
75 | });
76 | });
77 |
78 | describe(@"#startLoading", ^{
79 | context(@"when the protocol receives a request", ^{
80 | __block NSString *stringUrl = nil;
81 | __block NSString *bodyString = nil;
82 | __block LSHTTPStubURLProtocol *protocol = nil;
83 | __block LSTestingNSURLProtocolClient *client = nil;
84 | beforeEach(^{
85 | stringUrl = @"http://api.example.com/dogs.xml";
86 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:stringUrl]];
87 |
88 | client = [[LSTestingNSURLProtocolClient alloc] init];
89 |
90 | protocol = [[LSHTTPStubURLProtocol alloc] initWithRequest:request cachedResponse:nil client:client];
91 | });
92 | context(@"that matches an stubbed request", ^{
93 | context(@"and the response should succeed", ^{
94 | beforeEach(^{
95 | LSStubRequest *stubRequest = [[LSStubRequest alloc] initWithMethod:@"GET" url:stringUrl];
96 | LSStubResponse *stubResponse = [[LSStubResponse alloc] initWithStatusCode:403];
97 | [stubResponse setHeader:@"Content-Type" value:@"application/xml"];
98 | bodyString = @"Error" ;
99 | stubResponse.body = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
100 | stubRequest.response = stubResponse;
101 |
102 | [[LSNocilla sharedInstance] stub:@selector(stubbedRequests) andReturn:@[stubRequest]];
103 | });
104 |
105 | it(@"should pass to the client the correct response", ^{
106 | [protocol startLoading];
107 |
108 | [[client.response should] beKindOfClass:[NSHTTPURLResponse class]];
109 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)client.response;
110 | [[response.URL should] equal:[NSURL URLWithString:stringUrl]];
111 | [[theValue(response.statusCode) should] equal:theValue(403)];
112 | [[response.allHeaderFields should] equal:@{ @"Content-Type": @"application/xml"}];
113 | });
114 | it(@"should pass the body to the client", ^{
115 |
116 | [[client should] receive:@selector(URLProtocol:didLoadData:) withArguments:protocol, [bodyString dataUsingEncoding:NSUTF8StringEncoding]];
117 |
118 | [protocol startLoading];
119 |
120 | });
121 |
122 | it(@"should notify the client that it finished loading", ^{
123 | [[client should] receive:@selector(URLProtocolDidFinishLoading:)];
124 |
125 | [protocol startLoading];
126 | });
127 | });
128 |
129 | context(@"and the response should fail", ^{
130 | __block NSError *error;
131 | beforeEach(^{
132 | LSStubRequest *stubRequest = [[LSStubRequest alloc] initWithMethod:@"GET" url:stringUrl];
133 | error = [NSError mock];
134 | LSStubResponse *stubResponse = [[LSStubResponse alloc] initWithError:error];
135 | stubRequest.response = stubResponse;
136 |
137 | [[LSNocilla sharedInstance] stub:@selector(stubbedRequests) andReturn:@[stubRequest]];
138 | });
139 |
140 | it(@"should notify the client that it failed", ^{
141 | [[client should] receive:@selector(URLProtocol:didFailWithError:) withArguments:protocol, error];
142 |
143 | [protocol startLoading];
144 | });
145 | });
146 | });
147 | context(@"that doesn't match any stubbed request", ^{
148 | it(@"should raise an exception with a meaningful message", ^{
149 | NSString *expectedMessage = @"An unexpected HTTP request was fired.\n\nUse this snippet to stub the request:\nstubRequest(@\"GET\", @\"http://api.example.com/dogs.xml\");\n";
150 | [[theBlock(^{
151 | [protocol startLoading];
152 | }) should] raiseWithName:@"NocillaUnexpectedRequest" reason:expectedMessage];
153 | });
154 | });
155 | });
156 | });
157 |
158 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLRequest/MKNetworkKitStubbingSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "MKNetworkKit.h"
3 | #import "Nocilla.h"
4 | #import "HTTPServer.h"
5 | #import "LSTestingConnection.h"
6 |
7 | SPEC_BEGIN(MKNetworkKitStubbingSpec)
8 |
9 | beforeAll(^{
10 | [[LSNocilla sharedInstance] start];
11 | });
12 | afterAll(^{
13 | [[LSNocilla sharedInstance] stop];
14 | });
15 | afterEach(^{
16 | [[LSNocilla sharedInstance] clearStubs];
17 | });
18 |
19 | context(@"MKNetworkKit", ^{
20 | it(@"should stub an asynchronous request", ^{
21 | stubRequest(@"POST", @"http://example.com/say-hello").
22 | withHeaders(@{ @"X-MY-AWESOME-HEADER": @"sisisi", @"Content-Type": @"application/json; charset=utf-8" }).
23 | withBody(@"{\"text\":\"hola\"}").
24 | andReturn(200).
25 | withHeader(@"Content-Type", @"text/plain").
26 | withBody(@"{\"text\":\"adios\"}");
27 |
28 | MKNetworkOperation *operation = [[MKNetworkOperation alloc]
29 | initWithURLString:@"http://example.com/say-hello"
30 | params:[@{ @"text" : @"hola" } mutableCopy]
31 | httpMethod:@"POST"];
32 | [operation addHeaders: @{
33 | @"Content-Type" : @"text/plain",
34 | @"X-MY-AWESOME-HEADER" : @"sisisi" }];
35 |
36 | [operation setPostDataEncoding:MKNKPostDataEncodingTypeJSON];
37 | [operation start];
38 |
39 |
40 |
41 |
42 | [[expectFutureValue(operation.responseString) shouldEventually] equal:@"{\"text\":\"adios\"}"];
43 |
44 | [operation.error shouldBeNil];
45 | [[theValue(operation.readonlyResponse.statusCode) should] equal:theValue(200)];
46 | [[[operation.readonlyResponse.allHeaderFields objectForKey:@"Content-Type"] should] equal:@"text/plain"];
47 | });
48 |
49 | it(@"should have the same result as a real HTTP request", ^{
50 | NSURL *url = [NSURL URLWithString:@"http://localhost:12345/say-hello"];
51 |
52 | [[LSNocilla sharedInstance] stop];
53 |
54 | HTTPServer *server = [[HTTPServer alloc] init];
55 | [server setPort:12345];
56 | NSError *error;
57 | [server setConnectionClass:[LSTestingConnection class]];
58 | [server start:&error];
59 | [error shouldBeNil];
60 |
61 |
62 | MKNetworkOperation *realOperation = [[MKNetworkOperation alloc]
63 | initWithURLString:url.absoluteString
64 | params:[@{ @"this is" : @"the body" } mutableCopy]
65 | httpMethod:@"POST"];
66 | [realOperation addHeaders: @{
67 | @"Content-Type" : @"application/json",
68 | @"X-MY-AWESOME-HEADER" : @"sisisi" }];
69 |
70 | [realOperation setPostDataEncoding:MKNKPostDataEncodingTypeJSON];
71 | [realOperation start];
72 |
73 |
74 | [[expectFutureValue(theValue(realOperation.readonlyResponse.statusCode)) shouldEventually] equal:theValue(200)];
75 | [realOperation.error shouldBeNil];
76 | [[realOperation.responseString should] equal:@"hola"];
77 |
78 |
79 | NSHTTPURLResponse *realResponse = realOperation.readonlyResponse;
80 | NSString *realBody = realOperation.responseString;
81 |
82 | [server stop];
83 |
84 | [[LSNocilla sharedInstance] start];
85 |
86 | stubRequest(@"POST", @"http://localhost:12345/say-hello").
87 | withHeaders(@{ @"Content-Type": @"application/json; charset=utf-8", @"X-MY-AWESOME-HEADER": @"sisisi" }).
88 | withBody(@"{\"this is\":\"the body\"}").
89 | andReturn(200).
90 | withHeaders([realResponse allHeaderFields]).
91 | withBody(@"hola");
92 |
93 |
94 |
95 | MKNetworkOperation *stubbedOperation = [[MKNetworkOperation alloc]
96 | initWithURLString:url.absoluteString
97 | params:[@{ @"this is" : @"the body" } mutableCopy]
98 | httpMethod:@"POST"];
99 | [stubbedOperation addHeaders: @{
100 | @"Content-Type" : @"application/json",
101 | @"X-MY-AWESOME-HEADER" : @"sisisi" }];
102 |
103 | [stubbedOperation setPostDataEncoding:MKNKPostDataEncodingTypeJSON];
104 | [stubbedOperation start];
105 |
106 | [[expectFutureValue(theValue(stubbedOperation.readonlyResponse.statusCode)) shouldEventually] equal:theValue(200)];
107 |
108 | [[stubbedOperation.responseString should] equal:@"hola"];
109 | [stubbedOperation.error shouldBeNil];
110 |
111 |
112 |
113 | NSHTTPURLResponse *stubbedResponse = stubbedOperation.readonlyResponse;
114 | NSString *stubbedBody = stubbedOperation.responseString;
115 |
116 |
117 | NSDictionary *realHeaders = [realResponse allHeaderFields];
118 | NSDictionary *stubbedHeaders = [stubbedResponse allHeaderFields];
119 | [[theValue(realResponse.statusCode) should] equal:theValue(stubbedResponse.statusCode)];
120 | [[realHeaders should] equal:stubbedHeaders];
121 | [[realBody should] equal:stubbedBody];
122 | });
123 |
124 | });
125 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLRequest/NSURLRequest+LSHTTPRequestSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "NSURLRequest+LSHTTPRequest.h"
3 |
4 | SPEC_BEGIN(NSURLRequest_LSHTTPRequestSpec)
5 |
6 | describe(@"-body", ^{
7 | __block NSURLRequest *request;
8 |
9 | context(@"when the request provides a body stream", ^{
10 | __block NSString *stringUrl = @"http://api.example.com/dogs.xml";
11 | __block NSData *requestBody = [@"arg1=foo&arg2=bar" dataUsingEncoding:NSUTF8StringEncoding];
12 |
13 | beforeEach(^{
14 | NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:stringUrl]];
15 | mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithData:requestBody];
16 | request = mutableRequest;
17 | });
18 |
19 | it(@"uses the entire stream as the body", ^{
20 | [[[request body] should] equal:requestBody];
21 | });
22 | });
23 | });
24 |
25 | SPEC_END
26 |
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLRequest/NSURLRequestHookSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSNSURLHook.h"
3 | #import "LSHTTPStubURLProtocol.h"
4 |
5 | SPEC_BEGIN(LSNSURLHookSpec)
6 | describe(@"#load", ^{
7 | it(@"should register LSHTTPStubURLProtocol as a NSURLProtocol", ^{
8 | LSNSURLHook *hook = [[LSNSURLHook alloc] init];
9 | [[NSURLProtocol should] receive:@selector(registerClass:) withArguments:[LSHTTPStubURLProtocol class]];
10 |
11 | [hook load];
12 | });
13 | });
14 |
15 | describe(@"#unload", ^{
16 | it(@"should unregister LSHTTPStubURLProtocol as a NSURLProtocol", ^{
17 | LSNSURLHook *hook = [[LSNSURLHook alloc] init];
18 |
19 | [[NSURLProtocol should] receive:@selector(unregisterClass:) withArguments:[LSHTTPStubURLProtocol class]];
20 |
21 | [hook unload];
22 | });
23 | });
24 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Hooks/NSURLSession/NSURLSessionStubbingSpec.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSURLSessionStubbingSpec.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 08/01/14.
6 | // Copyright 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "Kiwi.h"
10 | #import "Nocilla.h"
11 | #import "LSHTTPStubURLProtocol.h"
12 |
13 | SPEC_BEGIN(NSURLSessionStubbingSpec)
14 | if (NSClassFromString(@"NSURLSession") == nil) return;
15 |
16 | beforeEach(^{
17 | [[LSNocilla sharedInstance] start];
18 | });
19 | afterEach(^{
20 | [[LSNocilla sharedInstance] stop];
21 | [[LSNocilla sharedInstance] clearStubs];
22 | });
23 |
24 | it(@"stubs NSURLSessionDataTask", ^{
25 | stubRequest(@"GET", @"http://example.com")
26 | .andReturn(200)
27 | .withBody(@"this is a counter example.");
28 | __block BOOL done = NO;
29 | __block NSData *capturedData;
30 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
31 |
32 | NSURL *url = [NSURL URLWithString:@"http://example.com"];
33 | NSURLSession *urlsession = [NSURLSession sessionWithConfiguration:configuration];
34 | NSURLSessionDataTask *datatask = [urlsession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
35 | done = YES;
36 | capturedData = data;
37 | }];
38 | [datatask resume];
39 |
40 |
41 | [[expectFutureValue(theValue(done)) shouldEventually] beYes];
42 | [[[[NSString alloc] initWithData:capturedData encoding:NSUTF8StringEncoding] should] equal:@"this is a counter example."];
43 | });
44 |
45 | SPEC_END
46 |
--------------------------------------------------------------------------------
/NocillaTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.luissolano.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/NocillaTests/LSNocillaSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSNocilla.h"
3 | #import "LSStubRequest.h"
4 | #import "LSStubResponse.h"
5 |
6 | SPEC_BEGIN(LSNocillaSpec)
7 |
8 | describe(@"-responseForRequest:", ^{
9 | context(@"when the specified request matches a previously stubbed request", ^{
10 | it(@"returns the associated response", ^{
11 | LSStubRequest *stubbedRequest = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
12 | LSStubResponse *stubbedResponse = [[LSStubResponse alloc] initWithStatusCode:403];
13 | stubbedRequest.response = stubbedResponse;
14 | [[LSNocilla sharedInstance] addStubbedRequest:stubbedRequest];
15 |
16 | NSObject *actualRequest = [KWMock nullMockForProtocol:@protocol(LSHTTPRequest)];
17 | [actualRequest stub:@selector(url) andReturn:[NSURL URLWithString:@"http://www.google.com"]];
18 | [actualRequest stub:@selector(method) andReturn:@"GET"];
19 |
20 | LSStubResponse *result = [[LSNocilla sharedInstance] responseForRequest:actualRequest];
21 |
22 | [[result should] equal:stubbedResponse];
23 |
24 | [[LSNocilla sharedInstance] clearStubs];
25 | });
26 |
27 | describe(@"when a stubbed request is replaced with a new stub", ^{
28 | it(@"returns the response for the newer stub", ^{
29 | LSStubRequest *firstStub = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
30 | LSStubResponse *firstResponse = [[LSStubResponse alloc] initWithStatusCode:403];
31 | firstStub.response = firstResponse;
32 | [[LSNocilla sharedInstance] addStubbedRequest:firstStub];
33 |
34 | LSStubRequest *secondStub = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"http://www.google.com"];
35 | LSStubResponse *secondResponse = [[LSStubResponse alloc] initWithStatusCode:200];
36 | secondStub.response = secondResponse;
37 | [[LSNocilla sharedInstance] addStubbedRequest:secondStub];
38 |
39 | NSObject *actualRequest = [KWMock nullMockForProtocol:@protocol(LSHTTPRequest)];
40 | [actualRequest stub:@selector(url) andReturn:[NSURL URLWithString:@"http://www.google.com"]];
41 | [actualRequest stub:@selector(method) andReturn:@"GET"];
42 |
43 | LSStubResponse *result = [[LSNocilla sharedInstance] responseForRequest:actualRequest];
44 |
45 | [[result shouldNot] equal:firstResponse];
46 | [[result should] equal:secondResponse];
47 |
48 | [[LSNocilla sharedInstance] clearStubs];
49 | });
50 | });
51 | });
52 |
53 | context(@"when the specified request does not match any stubbed request", ^{
54 | it(@"raises an exception with a descriptive error message ", ^{
55 | NSObject *actualRequest = [KWMock nullMockForProtocol:@protocol(LSHTTPRequest)];
56 | [actualRequest stub:@selector(url) andReturn:[NSURL URLWithString:@"http://www.google.com"]];
57 | [actualRequest stub:@selector(method) andReturn:@"GET"];
58 |
59 |
60 | NSString *expectedMessage = @"An unexpected HTTP request was fired.\n\nUse this snippet to stub the request:\nstubRequest(@\"GET\", @\"http://www.google.com\");\n";
61 | [[theBlock(^{
62 | [[LSNocilla sharedInstance] responseForRequest:actualRequest];
63 | }) should] raiseWithName:@"NocillaUnexpectedRequest" reason:expectedMessage];
64 |
65 | [[LSNocilla sharedInstance] clearStubs];
66 | });
67 | });
68 | });
69 |
70 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/LSStubResponseSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSStubResponse.h"
3 |
4 | SPEC_BEGIN(LSStubResponseSpec)
5 |
6 | describe(@"#initWithRawResponse:", ^{
7 | context(@"when the input data is a complete raw response", ^{
8 | it(@"should have the correct statusCode, headers and body", ^{
9 | NSData *rawData = [@"HTTP/1.1 201 created\nContent-Type: application/json; charset=utf-8\nConnection: keep-alive\n\n{\"success\":true}" dataUsingEncoding:NSUTF8StringEncoding];
10 | LSStubResponse *stubResponse = [[LSStubResponse alloc] initWithRawResponse:rawData];
11 |
12 | [[theValue(stubResponse.statusCode) should] equal:theValue(201)];
13 | [[stubResponse.headers should] equal:[NSDictionary dictionaryWithObjectsAndKeys:@"application/json; charset=utf-8", @"Content-Type",
14 | @"keep-alive", @"Connection", nil]];
15 | [[stubResponse.body should] equal:[@"{\"success\":true}" dataUsingEncoding:NSUTF8StringEncoding]];
16 | });
17 | });
18 |
19 | context(@"when the input data is a just the raw body", ^{
20 | it(@"should have the correct body with default status code and headers", ^{
21 | NSData *rawData = [@"{\"success\":true}" dataUsingEncoding:NSUTF8StringEncoding];
22 | LSStubResponse *stubResponse = [[LSStubResponse alloc] initWithRawResponse:rawData];
23 |
24 | [[theValue(stubResponse.statusCode) should] equal:theValue(200)];
25 | [[stubResponse.headers should] equal:[NSDictionary dictionary]];
26 | [[stubResponse.body should] equal:[@"{\"success\":true}" dataUsingEncoding:NSUTF8StringEncoding]];
27 | });
28 | });
29 | });
30 |
31 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/LSTestRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "LSHTTPRequest.h"
4 |
5 | @interface LSTestRequest : NSObject
6 |
7 | @property (nonatomic, strong) NSURL *url;
8 | @property (nonatomic, strong) NSString *method;
9 | @property (nonatomic, readonly) NSDictionary *headers;
10 | @property (nonatomic, strong) NSData *body;
11 |
12 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url;
13 | - (void)setHeader:(NSString *)header value:(NSString *)value;
14 | @end
15 |
--------------------------------------------------------------------------------
/NocillaTests/LSTestRequest.m:
--------------------------------------------------------------------------------
1 | #import "LSTestRequest.h"
2 |
3 | @interface LSTestRequest ()
4 | @property (nonatomic, strong) NSMutableDictionary *mutableHeaders;
5 | @end
6 |
7 | @implementation LSTestRequest
8 |
9 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url {
10 | self = [super init];
11 | if (self) {
12 | _method = method;
13 | _url = [NSURL URLWithString:url];
14 | _mutableHeaders = [NSMutableDictionary dictionary];
15 | }
16 | return self;
17 | }
18 |
19 | - (void)setHeader:(NSString *)header value:(NSString *)value {
20 | self.mutableHeaders[header] = value;
21 | }
22 |
23 | - (NSDictionary *)headers {
24 | return [self.mutableHeaders copy];
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/NocillaTests/Matchers/LSDataMatcherSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSDataMatcher.h"
3 |
4 | SPEC_BEGIN(LSDataMatcherSpec)
5 |
6 | __block LSDataMatcher *matcher = nil;
7 | __block NSData *data = nil;
8 |
9 | beforeEach(^{
10 | const char bytes[] = { 0xF1, 0x00, 0xFF };
11 | data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
12 | matcher = [[LSDataMatcher alloc] initWithData:data];
13 | });
14 |
15 | context(@"when both data are equal", ^{
16 | it(@"matches", ^{
17 | [[theValue([matcher matchesData:[data copy]]) should] beYes];
18 | });
19 | });
20 |
21 | context(@"when both data are different", ^{
22 | it(@"does not match", ^{
23 | const char other_bytes[] = { 0xA1, 0x00, 0xAF };
24 | NSData *other_data = [NSData dataWithBytes:other_bytes length:sizeof(other_bytes)];
25 |
26 | [[theValue([matcher matchesData:other_data]) should] beNo];
27 | });
28 | });
29 |
30 | describe(@"isEqual:", ^{
31 | it(@"is equal to another data matcher with the same data", ^{
32 | LSDataMatcher *matcherA = [[LSDataMatcher alloc] initWithData:[@"same" dataUsingEncoding:NSUTF8StringEncoding]];
33 | LSDataMatcher *matcherB = [[LSDataMatcher alloc] initWithData:[@"same" dataUsingEncoding:NSUTF8StringEncoding]];
34 |
35 | [[matcherA should] equal:matcherB];
36 | });
37 |
38 | it(@"is not equal to another data matcher with a different data", ^{
39 | LSDataMatcher *matcherA = [[LSDataMatcher alloc] initWithData:[@"omg" dataUsingEncoding:NSUTF8StringEncoding]];
40 | LSDataMatcher *matcherB = [[LSDataMatcher alloc] initWithData:[@"different" dataUsingEncoding:NSUTF8StringEncoding]];
41 |
42 | [[matcherA shouldNot] equal:matcherB];
43 | });
44 | });
45 |
46 | SPEC_END
47 |
--------------------------------------------------------------------------------
/NocillaTests/Matchers/LSRegexMatcherSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSRegexMatcher.h"
3 | #import "NSString+Nocilla.h"
4 |
5 | SPEC_BEGIN(LSRegexMatcherSpec)
6 |
7 | __block LSRegexMatcher *matcher = nil;
8 | beforeEach(^{
9 | matcher = [[LSRegexMatcher alloc] initWithRegex:@"Fo+".regex];
10 | });
11 |
12 | context(@"when the string matches the regex", ^{
13 | it(@"matches", ^{
14 | [[theValue([matcher matches:@"Fo"]) should] beYes];
15 | [[theValue([matcher matches:@"Foo"]) should] beYes];
16 | [[theValue([matcher matches:@"Foooooo"]) should] beYes];
17 | });
18 | });
19 |
20 | context(@"when string does not match", ^{
21 | it(@"does not match", ^{
22 | [[theValue([matcher matches:@"fo"]) should] beNo];
23 | [[theValue([matcher matches:@"F"]) should] beNo];
24 | [[theValue([matcher matches:@"bar"]) should] beNo];
25 | });
26 | });
27 |
28 | describe(@"isEqual:", ^{
29 | it(@"is equal to another regex matcher with the same regex", ^{
30 | LSRegexMatcher *matcherA = [[LSRegexMatcher alloc] initWithRegex:@"([same]+)".regex];
31 | LSRegexMatcher *matcherB = [[LSRegexMatcher alloc] initWithRegex:@"([same]+)".regex];
32 |
33 | [[matcherA should] equal:matcherB];
34 | });
35 |
36 | it(@"is not equal to another regex matcher with a different regex", ^{
37 | LSRegexMatcher *matcherA = [[LSRegexMatcher alloc] initWithRegex:@"([omg]+)".regex];
38 | LSRegexMatcher *matcherB = [[LSRegexMatcher alloc] initWithRegex:@"([different]+)".regex];
39 |
40 | [[matcherA shouldNot] equal:matcherB];
41 | });
42 | });
43 |
44 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Matchers/LSStringMatcherSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSStringMatcher.h"
3 |
4 | SPEC_BEGIN(LSStringMatcherSpec)
5 |
6 | __block LSStringMatcher *matcher = nil;
7 | beforeEach(^{
8 | matcher = [[LSStringMatcher alloc] initWithString:@"foo"];
9 | });
10 |
11 | context(@"when both strings are equal", ^{
12 | it(@"matches", ^{
13 | [[theValue([matcher matches:@"foo"]) should] beYes];
14 | });
15 | });
16 |
17 | context(@"when both strings are different", ^{
18 | it(@"does not match", ^{
19 | [[theValue([matcher matches:@"bar"]) should] beNo];
20 | });
21 | });
22 |
23 | describe(@"isEqual:", ^{
24 | it(@"is equal to another string matcher with the same string", ^{
25 | LSStringMatcher *matcherA = [[LSStringMatcher alloc] initWithString:@"same"];
26 | LSStringMatcher *matcherB = [[LSStringMatcher alloc] initWithString:@"same"];
27 |
28 | [[matcherA should] equal:matcherB];
29 | });
30 |
31 | it(@"is not equal to another string matcher with a different string", ^{
32 | LSStringMatcher *matcherA = [[LSStringMatcher alloc] initWithString:@"omg"];
33 | LSStringMatcher *matcherB = [[LSStringMatcher alloc] initWithString:@"different"];
34 |
35 | [[matcherA shouldNot] equal:matcherB];
36 | });
37 | });
38 |
39 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/NocillaTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/NocillaTests/Stubs/LSStubRequestSpec.m:
--------------------------------------------------------------------------------
1 | #import "Kiwi.h"
2 | #import "LSStubRequest.h"
3 | #import "LSTestRequest.h"
4 | #import "NSString+Nocilla.h"
5 | #import "LSRegexMatcher.h"
6 | #import "LSDataMatcher.h"
7 | #import "LSStringMatcher.h"
8 |
9 | SPEC_BEGIN(LSStubRequestSpec)
10 |
11 | describe(@"#matchesRequest:", ^{
12 | context(@"when the method and the URL are equal", ^{
13 | it(@"should match", ^{
14 | LSStubRequest *stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
15 |
16 | LSTestRequest *other = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
17 |
18 | [[theValue([stubRequest matchesRequest:other]) should] beYes];
19 | });
20 | });
21 | context(@"when the method is different", ^{
22 | it(@"should not match", ^{
23 | LSStubRequest *stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
24 |
25 | LSTestRequest *other = [[LSTestRequest alloc] initWithMethod:@"GET" url:@"https://api.example.com/cats/whiskas.json"];
26 |
27 | [[theValue([stubRequest matchesRequest:other]) should] beNo];
28 | });
29 | });
30 | context(@"when the URL is different", ^{
31 | it(@"should not match", ^{
32 | LSStubRequest *stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
33 |
34 | LSTestRequest *other = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/dogs/barky.json"];
35 |
36 | [[theValue([stubRequest matchesRequest:other]) should] beNo];
37 | });
38 | });
39 |
40 | context(@"when we use a regex matcher for the URL", ^{
41 | __block LSStubRequest *stubRequest = nil;
42 | __block LSTestRequest *other = nil;
43 | beforeEach(^{
44 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" urlMatcher:[[LSRegexMatcher alloc] initWithRegex:@"^http://foo.com".regex]];
45 | });
46 | context(@"the the actual URL matches that regex", ^{
47 | it(@"matches", ^{
48 | other = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"http://foo.com/something.json"];
49 |
50 | [[theValue([stubRequest matchesRequest:other]) should] beYes];
51 | });
52 | });
53 |
54 | context(@"the the actual URL matches that regex", ^{
55 | it(@"matches", ^{
56 | other = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"asdhttp://foo.com"];
57 |
58 | [[theValue([stubRequest matchesRequest:other]) should] beNo];
59 | });
60 | });
61 | });
62 |
63 | context(@"when the stub request has a subset of header of the actual request", ^{
64 | __block LSStubRequest *stubRequest = nil;
65 | __block LSTestRequest *actualRequest = nil;
66 | beforeEach(^{
67 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
68 |
69 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
70 |
71 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
72 |
73 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
74 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
75 | });
76 | describe(@"the stubRequest request", ^{
77 | it(@"should match the actualRequest request", ^{
78 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beYes];
79 | });
80 | });
81 |
82 | });
83 |
84 | context(@"when the stub request has a perset of headers of the actual request", ^{
85 | __block LSStubRequest *stubRequest = nil;
86 | __block LSTestRequest *actualRequest = nil;
87 | beforeEach(^{
88 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
89 |
90 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
91 |
92 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
93 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
94 |
95 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
96 | });
97 | describe(@"the actualRequest request", ^{
98 | it(@"should not match the stubRequest request", ^{
99 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beNo];
100 | });
101 | });
102 | });
103 |
104 | context(@"when the both requests have the same headers", ^{
105 | __block LSStubRequest *stubRequest = nil;
106 | __block LSTestRequest *actualRequest = nil;
107 | beforeEach(^{
108 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
109 |
110 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
111 |
112 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
113 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
114 |
115 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
116 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
117 | });
118 | it(@"should match", ^{
119 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beYes];
120 | });
121 | });
122 |
123 | context(@"when both requests have the same body", ^{
124 | __block LSStubRequest *stubRequest = nil;
125 | __block LSTestRequest *actualRequest = nil;
126 | beforeEach(^{
127 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
128 |
129 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
130 |
131 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
132 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
133 |
134 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
135 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
136 |
137 | LSDataMatcher *bodyMatcher = [[LSDataMatcher alloc] initWithData:[@"Hola, this is a body" dataUsingEncoding:NSUTF8StringEncoding]];
138 | [stubRequest setBody:bodyMatcher];
139 | [actualRequest setBody:[@"Hola, this is a body" dataUsingEncoding:NSUTF8StringEncoding]];
140 | });
141 | it(@"should match", ^{
142 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beYes];
143 | });
144 | });
145 |
146 | context(@"when using a matching regex to match the body", ^{
147 | __block LSStubRequest *stubRequest = nil;
148 | __block LSTestRequest *actualRequest = nil;
149 | beforeEach(^{
150 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
151 |
152 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
153 |
154 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
155 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
156 |
157 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
158 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
159 |
160 | LSRegexMatcher *bodyMatcher = [[LSRegexMatcher alloc] initWithRegex:[NSRegularExpression regularExpressionWithPattern:@"^Hola" options:0 error:nil]];
161 | [stubRequest setBody:bodyMatcher];
162 | [actualRequest setBody:[@"Hola, this is a body" dataUsingEncoding:NSUTF8StringEncoding]];
163 | });
164 | it(@"should match", ^{
165 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beYes];
166 | });
167 | });
168 |
169 | context(@"when both requests have different bodies", ^{
170 | __block LSStubRequest *stubRequest = nil;
171 | __block LSTestRequest *actualRequest = nil;
172 | beforeEach(^{
173 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
174 |
175 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
176 |
177 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
178 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
179 |
180 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
181 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
182 |
183 | LSDataMatcher *bodyMatcher = [[LSDataMatcher alloc] initWithData:[@"Hola, this is a body" dataUsingEncoding:NSUTF8StringEncoding]];
184 | [stubRequest setBody:bodyMatcher];
185 | [actualRequest setBody:[@"Adios, this is a body as well" dataUsingEncoding:NSUTF8StringEncoding]];
186 | });
187 | it(@"should match", ^{
188 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beNo];
189 | });
190 | });
191 |
192 | context(@"when using a regex that does no match to match the body", ^{
193 | __block LSStubRequest *stubRequest = nil;
194 | __block LSTestRequest *actualRequest = nil;
195 | beforeEach(^{
196 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
197 |
198 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
199 |
200 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
201 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
202 |
203 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
204 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
205 |
206 | LSRegexMatcher *bodyMatcher = [[LSRegexMatcher alloc] initWithRegex:[NSRegularExpression regularExpressionWithPattern:@"^Hola" options:0 error:nil]];
207 | [stubRequest setBody:bodyMatcher];
208 | [actualRequest setBody:[@"Adios, this is a body as well" dataUsingEncoding:NSUTF8StringEncoding]];
209 | });
210 | it(@"should match", ^{
211 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beNo];
212 | });
213 | });
214 |
215 | context(@"when the stubRequest request has a nil body", ^{
216 | __block LSStubRequest *stubRequest = nil;
217 | __block LSTestRequest *actualRequest = nil;
218 | beforeEach(^{
219 | stubRequest = [[LSStubRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
220 |
221 | actualRequest = [[LSTestRequest alloc] initWithMethod:@"PUT" url:@"https://api.example.com/cats/whiskas.json"];
222 |
223 | [stubRequest setHeader:@"Content-Type" value:@"application/json"];
224 | [stubRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
225 |
226 | [actualRequest setHeader:@"Content-Type" value:@"application/json"];
227 | [actualRequest setHeader:@"X-API-TOKEN" value:@"123abc"];
228 |
229 | [actualRequest setBody:[@"Adios, this is a body as well" dataUsingEncoding:NSUTF8StringEncoding]];
230 | });
231 | describe(@"the stubRequest request", ^{
232 | it(@"should match the actualRequest request", ^{
233 | [[theValue([stubRequest matchesRequest:actualRequest]) should] beYes];
234 | });
235 | });
236 | });
237 | });
238 |
239 | describe(@"isEqual:", ^{
240 | it(@"is equal to a stub with the same method, url, headers and body", ^{
241 | // It may appear that there is needless duplication of matcher objects in this tests,
242 | // however this is intended as we need to ensure the properties of the stub request
243 | // are truly equal by value, not just equal pointers.
244 |
245 | LSRegexMatcher *urlA = [[LSRegexMatcher alloc] initWithRegex:@"https://(.+)\\.example\\.com/(.+)/data\\.json".regex];
246 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" urlMatcher:urlA];
247 | LSRegexMatcher *urlB = [[LSRegexMatcher alloc] initWithRegex:@"https://(.+)\\.example\\.com/(.+)/data\\.json".regex];
248 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"GET" urlMatcher:urlB];
249 |
250 | stubA.body = [[LSRegexMatcher alloc] initWithRegex:@"checkout my sexy body ([a-z]+?)".regex];
251 | stubB.body = [[LSRegexMatcher alloc] initWithRegex:@"checkout my sexy body ([a-z]+?)".regex];
252 |
253 | [stubA setHeader:@"Content-Type" value:@"application/json"];
254 | [stubB setHeader:@"Content-Type" value:@"application/json"];
255 |
256 | [[stubA should] equal:stubB];
257 | });
258 |
259 | it(@"is not equal with a different method", ^{
260 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
261 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"POST" url:@"google.com"];
262 |
263 | [[stubA shouldNot] equal:stubB];
264 | });
265 |
266 | it(@"is not equal with a different url matcher", ^{
267 | LSRegexMatcher *urlA = [[LSRegexMatcher alloc] initWithRegex:@"https://(.+)\\.example\\.com/(.+)/data\\.json".regex];
268 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" urlMatcher:urlA];
269 | LSRegexMatcher *urlB = [[LSRegexMatcher alloc] initWithRegex:@"https://github\\.com/(.+)/(.+)/".regex];
270 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"GET" urlMatcher:urlB];
271 |
272 | [[stubA shouldNot] equal:stubB];
273 | });
274 |
275 | it(@"is not equal with different headers", ^{
276 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
277 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
278 |
279 | [stubA setHeader:@"Content-Type" value:@"application/json"];
280 | [stubB setHeader:@"Content-Type" value:@"application/json"];
281 |
282 | [stubA setHeader:@"X-OMG" value:@"nothings compares"];
283 | [stubB setHeader:@"X-DIFFERENT" value:@"to you"];
284 |
285 | [[stubA shouldNot] equal:stubB];
286 | });
287 |
288 | it(@"is not equal with a different body", ^{
289 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
290 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
291 |
292 | stubA.body = [[LSStringMatcher alloc] initWithString:@"omg"];
293 | stubB.body = [[LSStringMatcher alloc] initWithString:@"different"];
294 |
295 | [[stubA shouldNot] equal:stubB];
296 | });
297 |
298 | it(@"is equal with a nil bodies", ^{
299 | // Body is special in that it's the only propery that may be nil,
300 | // and [nil isEqual:nil] returns NO.
301 |
302 | LSStubRequest *stubA = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
303 | LSStubRequest *stubB = [[LSStubRequest alloc] initWithMethod:@"GET" url:@"google.com"];
304 |
305 | stubA.body = nil;
306 | stubB.body = nil;
307 |
308 | [[stubA should] equal:stubB];
309 | });
310 | });
311 |
312 | SPEC_END
--------------------------------------------------------------------------------
/NocillaTests/Support/LSTestingConnection.h:
--------------------------------------------------------------------------------
1 | #import "HTTPConnection.h"
2 | #import "LSStubResponse.h"
3 |
4 | @interface LSTestingConnection : HTTPConnection
5 | @end
6 |
--------------------------------------------------------------------------------
/NocillaTests/Support/LSTestingConnection.m:
--------------------------------------------------------------------------------
1 | #import "LSTestingConnection.h"
2 | #import "HTTPDataResponse.h"
3 | #import "HTTPResponse.h"
4 | @implementation LSTestingConnection
5 | - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path {
6 | return YES;
7 | }
8 |
9 | - (NSObject *)httpResponseForMethod:(NSString *)method URI:(NSString *)path {
10 | NSObject *response = [[HTTPDataResponse alloc] initWithData:[@"hola" dataUsingEncoding:NSUTF8StringEncoding]];
11 |
12 | return response;
13 | }
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/NocillaTests/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | platform :ios, '9.0'
4 |
5 | project 'Nocilla'
6 |
7 | target :NocillaTests do
8 | inherit! :search_paths
9 |
10 | pod 'MKNetworkKit', '~> 0.87'
11 | pod 'AFNetworking', '~> 2.4.1'
12 | pod 'CocoaHTTPServer', '~> 2.2.1'
13 | pod 'Kiwi', '~> 2.3.0'
14 | pod 'ASIHTTPRequest', '>= 1.8.1'
15 | end
16 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFNetworking (2.4.1):
3 | - AFNetworking/NSURLConnection (= 2.4.1)
4 | - AFNetworking/NSURLSession (= 2.4.1)
5 | - AFNetworking/Reachability (= 2.4.1)
6 | - AFNetworking/Security (= 2.4.1)
7 | - AFNetworking/Serialization (= 2.4.1)
8 | - AFNetworking/UIKit (= 2.4.1)
9 | - AFNetworking/NSURLConnection (2.4.1):
10 | - AFNetworking/Reachability
11 | - AFNetworking/Security
12 | - AFNetworking/Serialization
13 | - AFNetworking/NSURLSession (2.4.1):
14 | - AFNetworking/Reachability
15 | - AFNetworking/Security
16 | - AFNetworking/Serialization
17 | - AFNetworking/Reachability (2.4.1)
18 | - AFNetworking/Security (2.4.1)
19 | - AFNetworking/Serialization (2.4.1)
20 | - AFNetworking/UIKit (2.4.1):
21 | - AFNetworking/NSURLConnection
22 | - AFNetworking/NSURLSession
23 | - ASIHTTPRequest (1.8.1):
24 | - ASIHTTPRequest/ASIWebPageRequest (= 1.8.1)
25 | - ASIHTTPRequest/CloudFiles (= 1.8.1)
26 | - ASIHTTPRequest/Core (= 1.8.1)
27 | - ASIHTTPRequest/S3 (= 1.8.1)
28 | - ASIHTTPRequest/ASIWebPageRequest (1.8.1):
29 | - ASIHTTPRequest/Core
30 | - ASIHTTPRequest/CloudFiles (1.8.1):
31 | - ASIHTTPRequest/Core
32 | - ASIHTTPRequest/Core (1.8.1):
33 | - Reachability
34 | - ASIHTTPRequest/S3 (1.8.1):
35 | - ASIHTTPRequest/Core
36 | - CocoaAsyncSocket (0.0.1)
37 | - CocoaHTTPServer (2.2.1):
38 | - CocoaAsyncSocket (~> 0.0.1)
39 | - CocoaLumberjack (~> 1.3.0)
40 | - CocoaLumberjack (1.3.3)
41 | - Kiwi (2.3.1)
42 | - MKNetworkKit (0.87):
43 | - Reachability (~> 3.1.0)
44 | - Reachability (3.1.1)
45 |
46 | DEPENDENCIES:
47 | - AFNetworking (~> 2.4.1)
48 | - ASIHTTPRequest (>= 1.8.1)
49 | - CocoaHTTPServer (~> 2.2.1)
50 | - Kiwi (~> 2.3.0)
51 | - MKNetworkKit (~> 0.87)
52 |
53 | SPEC CHECKSUMS:
54 | AFNetworking: 41d45d2c14219af0fa5addf3cfd433a002c13c9f
55 | ASIHTTPRequest: bf4f9b8be3526d40865d045bb664e0bab246fb81
56 | CocoaAsyncSocket: e2688ff99f2c2dbbeb95a1d02ba7dd878726ac83
57 | CocoaHTTPServer: 36f27f3fbc03d25a4fafcc22409a944c5bcf0d2d
58 | CocoaLumberjack: 9c1e36cb5bf886ad691191f59871a11179c7c2aa
59 | Kiwi: f038a6c61f7a9e4d7766bff5717aa3b3fdb75f55
60 | MKNetworkKit: 60ad3c83b62183e40ba75ce772c9427c25b520de
61 | Reachability: dd9aa4fb6667b9f787690a74f53cb7634ce99893
62 |
63 | PODFILE CHECKSUM: 6845bdf1d8a193a8186fdae487e0615d7037f7ba
64 |
65 | COCOAPODS: 1.0.1
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nocilla [](https://travis-ci.org/luisobo/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)
2 |
3 | Stunning HTTP stubbing for iOS and OS X. Testing HTTP requests has never been easier.
4 |
5 | This library was inspired by [WebMock](https://github.com/bblimke/webmock) and it's using [this approach](http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/) to stub the requests.
6 |
7 | ## Features
8 | * Stub HTTP and HTTPS requests in your unit tests.
9 | * Supports NSURLConnection, NSURLSession and ASIHTTPRequest.
10 | * Awesome DSL that will improve the readability and maintainability of your tests.
11 | * Match requests with regular expressions.
12 | * Stub requests with errors.
13 | * Tested.
14 | * Fast.
15 | * Extendable to support more HTTP libraries.
16 |
17 | ## Installation
18 | ### As a [CocoaPod](http://cocoapods.org/)
19 | Just add this to your Podfile
20 | ```ruby
21 | pod 'Nocilla'
22 | ```
23 |
24 | ### Other approaches
25 | * You should be able to add Nocilla to you source tree. If you are using git, consider using a `git submodule`
26 |
27 | ## Usage
28 | _Yes, the following code is valid Objective-C, or at least, it should be_
29 |
30 | The following examples are described using [Kiwi](https://github.com/kiwi-bdd/Kiwi)
31 |
32 | ### Common parts
33 | Until Nocilla can hook directly into Kiwi, you will have to include the following snippet in the specs you want to use Nocilla:
34 |
35 | ```objc
36 | #import "Kiwi.h"
37 | #import "Nocilla.h"
38 | SPEC_BEGIN(ExampleSpec)
39 | beforeAll(^{
40 | [[LSNocilla sharedInstance] start];
41 | });
42 | afterAll(^{
43 | [[LSNocilla sharedInstance] stop];
44 | });
45 | afterEach(^{
46 | [[LSNocilla sharedInstance] clearStubs];
47 | });
48 |
49 | it(@"should do something", ^{
50 | // Stub here!
51 | });
52 | SPEC_END
53 | ```
54 |
55 | ### Stubbing requests
56 | #### Stubbing a simple request
57 | It will return the default response, which is a 200 and an empty body.
58 |
59 | ```objc
60 | stubRequest(@"GET", @"http://www.google.com");
61 | ```
62 |
63 | #### Stubbing requests with regular expressions
64 | ```objc
65 | stubRequest(@"GET", @"^http://(.*?)\\.example\\.com/v1/dogs\\.json".regex);
66 | ```
67 |
68 |
69 | #### Stubbing a request with a particular header
70 |
71 | ```objc
72 | stubRequest(@"GET", @"https://api.example.com").
73 | withHeader(@"Accept", @"application/json");
74 | ```
75 |
76 | #### Stubbing a request with multiple headers
77 |
78 | Using the `withHeaders` method makes sense with the Objective-C literals, but it accepts an NSDictionary.
79 |
80 | ```objc
81 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
82 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"});
83 | ```
84 |
85 | #### Stubbing a request with a particular body
86 |
87 | ```objc
88 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
89 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
90 | withBody(@"{\"name\":\"foo\"}");
91 | ```
92 |
93 | You can also use `NSData` for the request body:
94 |
95 | ```objc
96 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
97 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
98 | withBody([@"foo" dataUsingEncoding:NSUTF8StringEncoding]);
99 | ```
100 |
101 | It even works with regular expressions!
102 |
103 | ```objc
104 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
105 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
106 | withBody(@"^The body start with this".regex);
107 | ```
108 |
109 | #### Returning a specific status code
110 | ```objc
111 | stubRequest(@"GET", @"http://www.google.com").andReturn(404);
112 | ```
113 |
114 | #### Returning a specific status code and header
115 | The same approch here, you can use `withHeader` or `withHeaders`
116 |
117 | ```objc
118 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
119 | andReturn(201).
120 | withHeaders(@{@"Content-Type": @"application/json"});
121 | ```
122 |
123 | #### Returning a specific status code, headers and body
124 | ```objc
125 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
126 | andReturn(201).
127 | withHeaders(@{@"Content-Type": @"application/json"}).
128 | withBody(@"{\"ok\":true}");
129 | ```
130 |
131 | You can also use `NSData` for the response body:
132 |
133 | ```objc
134 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
135 | andReturn(201).
136 | withHeaders(@{@"Content-Type": @"application/json"}).
137 | withBody([@"bar" dataUsingEncoding:NSUTF8StringEncoding]);
138 | ```
139 |
140 | #### Returning raw responses recorded with `curl -is`
141 | `curl -is http://api.example.com/dogs.json > /tmp/example_curl_-is_output.txt`
142 |
143 | ```objc
144 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
145 | andReturnRawResponse([NSData dataWithContentsOfFile:@"/tmp/example_curl_-is_output.txt"]);
146 | ```
147 |
148 | #### All together
149 | ```objc
150 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
151 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
152 | withBody(@"{\"name\":\"foo\"}").
153 | andReturn(201).
154 | withHeaders(@{@"Content-Type": @"application/json"}).
155 | withBody(@"{\"ok\":true}");
156 | ```
157 |
158 | #### Making a request fail
159 | This will call the failure handler (callback, delegate... whatever your HTTP client uses) with the specified error.
160 |
161 | ```objc
162 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
163 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
164 | withBody(@"{\"name\":\"foo\"}").
165 | andFailWithError([NSError errorWithDomain:@"foo" code:123 userInfo:nil]);
166 | ```
167 |
168 | #### Replacing a request stub
169 |
170 | If you need to change the response of a single request, simply re-stub the request:
171 |
172 | ```objc
173 | stubRequest(@"POST", @"https://api.example.com/authorize/").
174 | andReturn(401);
175 |
176 | // Some test expectation...
177 |
178 | stubRequest(@"POST", @"https://api.example.com/authorize/").
179 | andReturn(200);
180 | ```
181 |
182 | ### Unexpected requests
183 | If some request is made but it wasn't stubbed, Nocilla won't let that request hit the real world. In that case your test should fail.
184 | At this moment Nocilla will raise an exception with a meaningful message about the error and how to solve it, including a snippet of code on how to stub the unexpected request.
185 |
186 | ### Testing asynchronous requests
187 | When testing asynchrounous requests your request will be sent on a different thread from the one on which your test is executed. It is important to keep this in mind, and design your test in such a way that is has enough time to finish. For instance ```tearDown()``` when using ```XCTest``` and ```afterEach()``` when using [Quick](https://github.com/Quick/Quick) and [Nimble](https://github.com/Quick/Nimble) will cause the request never to complete.
188 |
189 |
190 | ## Who uses Nocilla.
191 |
192 | ### Submit a PR to add your company here!
193 |
194 | - [MessageBird](https://www.messagebird.com)
195 | - [Groupon](http://www.groupon.com)
196 | - [Pixable](http://www.pixable.com)
197 | - [Jackthreads](https://www.jackthreads.com)
198 | - [ShopKeep](http://www.shopkeep.com)
199 | - [Venmo](https://www.venmo.com)
200 | - [Lighthouse](http://www.lighthouselabs.co.uk)
201 | - [GE Digital](http://www.ge.com/digital/)
202 | - [Jobandtalent](http://www.jobandtalent.com)
203 |
204 | ## Other alternatives
205 | * [ILTesting](https://github.com/InfiniteLoopDK/ILTesting)
206 | * [OHHTTPStubs](https://github.com/AliSoftware/OHHTTPStubs)
207 |
208 | ## Contributing
209 |
210 | 1. Fork it
211 | 2. Create your feature branch
212 | 3. Commit your changes
213 | 4. Push to the branch
214 | 5. Create new Pull Request
215 |
--------------------------------------------------------------------------------