-
Notifications
You must be signed in to change notification settings - Fork 602
Usage Examples
- Introduction: Reminder about Swift Closures
- Simple examples
- Bad and down network
- Advanced Usage and Tips
If you use OHHTTPStubs
in Swift, you can either use the ObjC API as-is, like OHHTTPStubs.stubRequestsPassingTest(,withStubResponse:)
, or use the Swift helper methods provided by the OHHTTPStubs/Swift
subspec.
I strongly suggest that you use the Swift helper methods, because they provide an API that is more idiomatic to Swift, taking advantage of trailing closures, functions and closures being interchangeable, and function operators.
For example, using the Swift helper methods, you can:
- use
stub(conditionClosure, response: responseClosure)
to create a stub, which is just a wrapper around a call toOHHTTPStubs.stubRequestsPassingTest(conditionClosure, withStubResponse: responseClosure)
, just shorter. - take advantage of the trailing closure syntax, by doing
stub({ req in someConditionOnReq }) { req in responseBlockCode }
- use the
isHost(host)
,isScheme(scheme)
, etc helpers that generate common closures to be used as the condition closure. You can for example usestub(isHost("mywebservice.com")) { req in … }
to stub only requests which host match"mywebservice.com"
.isHost(host)
returns a closure that returns true ifreq.URL?.host == host
. - combine those to generate more complex condition closures, like
isScheme("http") && isHost("mywebservice.com") && isPath("/foo/bar")
With the code below, only network requests to host "mywebservice.com"
will be stubbed, and they will return a stubbed response containing the data "Hello World!"
:
// Objective-C
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
// Stub all those requests with "Hello World!" string
NSData* stubData = [@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
return [OHHTTPStubsResponse responseWithData:stubData statusCode:200 headers:nil];
}];
// Swift
stub(isHost("mywebservice.com")) { _ in
let stubData = "Hello World!".dataUsingEncoding(NSUTF8StringEncoding)
return OHHTTPStubsResponse(data: stubData!, statusCode:200, headers:nil)
})
Note: it is not recommended to directly
return YES
in the first block (and thus stub all requests), because third-party frameworks & SDKs (likeCrashlytics
, etc) may also send requests on their own and you probably don't want them to receive your own stubbed response (which won't make sense for them anyway).
With the code below, the content of a given file in your application bundle will be used as a stub.
We use the OHPathForFile
global function provided in OHPathHelpers.h
as a convenience to easily create the path to the file in the app's bundle.
This is useful if you have added all your fixtures (stubbed responses for your Unit Tests) in your Xcode project and linked them with your Unit Test target.
// Objective-C
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
// Stub it with our "wsresponse.json" stub file
return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(@"wsresponse.json",self.class)
statusCode:200 headers:@{@"Content-Type":@"application/json"}];
}];
// Swift
stub(isHost("mywebservice.com")) { request in
// Stub it with our "wsresponse.json" stub file
return OHHTTPStubsResponse(
fileAtPath: OHPathForFile("wsresponse.json", type(of: self))!,
statusCode: 200,
headers: ["Content-Type":"application/json"]
)
}
Note: In Swift 2, use self.dynamicType
instead of the type(of: self)
Swift 3 syntax.
You may also put all your fixtures in a custom bundle (let's call it Fixtures.bundle
) and then use the helper functions to get it: OHPathForFileInBundle(@"wsresponse.json",OHResourceBundle(@"Fixtures", self.class))
.
Note: If you use a lot of fixtures, you may create a convenience macro like
#define fixture(x) OHPathForFileInBundle(@ #x ".json",OHResourceBundle(@"Fixtures", self.class))
so you can then simply usefixture(wsresponse.json)
in your ObjC code. As Swift does not support macros, there is no real equivalent in Swift.
As it is common for a lot of WebServices to use the JSON format in their response body, OHHTTPStubs
comes with a convenience method (defined as a category in OHHTTPStubs+JSON.h
) to build a response from a JSON object.
In practice, this convenience method simply use
NSJSONSerialization
to transform theNSDictionary
orNSArray
to JSON, and automatically adds theContent-Type: application/json
header if there is noContent-Type
header defined.
// Objective-C
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
NSDictionary* obj = @{ @"key1": @"value1", @"key2": @[@"value2A", @"value2B"] };
return [OHHTTPStubsResponse responseWithJSONObject:obj statusCode:200 headers:nil];
}];
In the following example, in addition to using shorthand parameter $0
in the first closure and using a trailing closure for the last parameter, we also use the anonymous parameter _
as we don't use the NSURLRequest
parameter in the closure.
// Swift
// Here we use the anonymous parameter '_' in the second closure (as its NSURLRequest parameter is unused in our code)
stub(isHost("mywebservice.com")) { _ in
let obj = ["key1":"value1", "key2":["value2A","value2B"]]
return OHHTTPStubsResponse(JSONObject: obj, statusCode: 200, headers: nil)
}
You can simulate a slow network by setting the requestTime
and/or responseTime
properties of your OHHTTPStubsResponse
.
This is useful to check that your user interface does not freeze when you have bad network conditions, and that you have all your activity indicators working while waiting for responses.
You may use the convenience chainable setters responseTime:
and requestTime:responseTime:
to set those values and easily chain method calls, like we will do in the following examples:
// Objective-C
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:@"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
return [[OHHTTPStubsResponse responseWithJSONObject:someDict statusCode:200 headers:nil]
requestTime:1.0 responseTime:3.0];
}];
// Swift
stub(isHost("mywebservice.com")) { _ in
return OHHTTPStubsResponse(JSONObject:someDict, statusCode:200, headers:nil)
.requestTime(1.0, responseTime: 3.0)
}
OHHTTPStubs
will wait requestTime
before sending the NSHTTPURLResponse
headers, and then start sending chunks of the stub data regularly during the period of responseTime
, to simulate the slow network.
In the end, you will only have the full content of your stub data after requestTime+responseTime
(time after which the completion
block or connectionDidFinishLoading:
delegate method will be called).
Note: You can specify a network speed instead of a
responseTime
by using a negative value. See below.
When building the OHHTTPStubsResponse
object, you can specify a response time (in seconds) so that the sending of the fake response will be spread over time. This allows you to simulate a slow network for example. (see "Set request and response time")
If you specify a negative value for the responseTime
parameter, instead of being interpreted as a time in seconds, it will be interpreted as a download speed in KBytes/s. In that case, the response time will be computed using the length of the response's data to simulate the indicated download speed.
The OHHTTPStubsResponse
header defines some constants for standard download speeds:
OHHTTPStubsDownloadSpeedGPRS = -7 = 7 KB/s = 56 kbps
OHHTTPStubsDownloadSpeedEDGE = -16 = 16 KB/s = 128 kbps
OHHTTPStubsDownloadSpeed3G = -400 = 400 KB/s = 3200 kbps
OHHTTPStubsDownloadSpeed3GPlus = -900 = 900 KB/s = 7200 kbps
OHHTTPStubsDownloadSpeedWifi = -1500 = 1500 KB/s = 12000 kbps
Example:
// Objective-C
return [[OHHTTPStubsResponse responseWithData:[NSData data] statusCode:400 headers:nil]
responseTime:OHHTTPStubsDownloadSpeed3G];
// Swift
return OHHTTPStubsResponse(data:NSData(), statusCode: 400, headers: nil)
.responseTime(OHHTTPStubsDownloadSpeed3G)
You may also return a network error for your stub. For example, you can easily simulate an absence of network connection like this:
// Objective-C
NSError* notConnectedError = [NSError errorWithDomain:NSURLErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:nil];
return [OHHTTPStubsResponse responseWithError:notConnectedError];
// Swift
let notConnectedError = NSError(domain: NSURLErrorDomain, code: URLError.notConnectedToInternet.rawValue)
return OHHTTPStubsResponse(error:notConnectedError)
Remember that the response block is a block so you can do anything you like in there. One useful thing to do could be to return different responses depending on several conditions.
One typical use case for that is to simulate one or multiple failures before finally succeeding. For example, one could return a failure response for the first 3 times the method is called, then a success the 4th time. Of course, you can imagine whatever other scenario you like, for example returning a network error the first time, then a 404 the next, then a 200.
// Objective-C
__block NSUInteger callCount = 0;
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
...
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
if (++callCount <= 2) {
// Return an error the first two times
NSError* notConnectedError = [NSError errorWithDomain:NSURLErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:nil];
return [OHHTTPStubsResponse responseWithError:notConnectedError];
} else {
// Return actual data afterwards
NSData* stubData = [@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
return [OHHTTPStubsResponse responseWithData:stubData statusCode:200 headers:nil];
}
}];
// Swift
var callCounter = 0
stub(…) {
callCounter += 1
if callCounter <= 2 {
let notConnectedError = NSError(domain:NSURLErrorDomain, code:Int(CFNetworkErrors.CFURLErrorNotConnectedToInternet.rawValue), userInfo:nil)
return OHHTTPStubsResponse(error:notConnectedError)
} else {
let stubData = "Hello World!".dataUsingEncoding(NSUTF8StringEncoding)
return OHHTTPStubsResponse(data: stubData!, statusCode:200, headers:nil)
}
}
- You can call
stubRequestsPassingTest:withStubResponse:
(ObjC) orstub(condition:)
(Swift) multiple times. It will just add the stubs in an internal list of stubs.
This may be useful to install different stubs in various places in your code, or to separate different stubbing conditions more easily. See the OHHTTPStubsDemo
project for a typical example. This may also be used for a fallback stub, as the first stub being installed will only be called if every other stub fails.
When a network request is performed by the system, the stubs are tested in the reverse order that they have been added, the last added stub having priority over the first added ones.
The first stub that returns YES
for the first parameter of stubRequestsPassingTest:withStubResponse:
(ObjC) / true
for the condition:
parameter of stub(condition:)
(Swift) is then used to reply to the request.
- You can then remove any given stub with the
removeStub:
method. This method takes as a parameter theid<OHHTTPStubsDescriptor>
object returned bystubRequestsPassingTest:withStubResponse:
/stub(condition:)
(Note: this returned object is already retained byOHHTTPStubs
while the stub is installed, so you should keep it in aweak
variable so it is properly released from memory once removed). - You can also remove all stubs at once with the
removeAllStubs
method.
This last one is useful when using OHHTTPStubs
in your Unit Tests, to remove all installed stubs at the end of each of your test case to avoid stubs installed in one test case to be still installed for the next test case.
// Objective-C
- (void)tearDown
{
[OHHTTPStubs removeAllStubs];
[super tearDown];
}
// Swift
func tearDown() {
OHHTTPStubs.removeAllStubs()
super.tearDown()
}
You can add a name of your choice to your stubs. The only purpose of this is to easily identify your stubs for debugging, like when displaying them in the console.
// Objective-C
__weak id<OHHTTPStubsDescriptor> stub = [OHHTTPStubs stubRequestsPassingTest:… withStubResponse:…];
stub.name = @"Stub for text files";
// Swift
weak var stub = stub(…) { … }
stub.name = "Stub for text files"
You can even imagine applying the .name = ...
affectation directly (if you don't need to use the returned id<OHHTTPStubsDescriptor>
otherwise), for a more concise syntax:
// Objective-C
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
...
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
...
}].name = @"Stub for text files";
// Swift
stub(…) { _ in
…
}.name = "Stub for text files"
You can then list all the installed stubs using [OHHTTPStubs allStubs]
(ObjC) / OHHTTPStubs.allStubs()
(Swift), which returns an array of objects conforming to OHHTTPStubsDescriptor
so you can display their name
on the console. This is useful to check that you didn't forget to remove some previous stubs that are still installed for example.
You can also setup a block that gets executed each time a request has been stubbed, using onStubActivation:
method. This is typically useful to log the stub being used for each request:
// Objective-C
[OHHTTPStubs onStubActivation:^(NSURLRequest *request, id<OHHTTPStubsDescriptor> stub) {
NSLog(@"%@ stubbed by %@.", request.URL, stub.name);
}];
// Swift
OHHTTPStubs.onStubActivation() { request, stub in
println("\(request.URL!) stubbed by \(stub.name).")
}
This page lists other examples provided by contributors who use OHHTTPStubs
for their projects to give you some idea of how you can use it in more interesting ways.