OHHTTPSTUBS
Bouchonner vos requêtes réseau facilement!
sans modifier le code de votre application

CocoaHeads Rennes!
20 février 2014

SmallTalk par O.Halligon
OBJECTIF
• Intercepter
• Retourner

les requêtes réseau sortantes!

une réponse toute faite à la place!

•

La requête ne part finalement pas sur le net!

•

On se substitue au serveur pour retourner la réponse qu’on veut
CAS D’USAGE
•

Tests Unitaires
•
•

•

Sans dépendre du réseau ou de la connectivité!
Avec des réponses déterministes et invariantes.

Tests d’asynchronisme en conditions dégradées
•
•

•

Simuler un réseau lent!
Vérifier que votre application ne gèle pas et affiche des spinners

Développement avant la livraison des BackEnds / WS
•

Développez vos modules d’appel aux WebServices

même si ceux-ci ne sont pas encore prêts
EXEMPLES
• Toutes les requêtes retournent un texte constant

[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {

!
!
}
!
!

return YES;
withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {

}];

NSData* stubData = [@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding];
return [OHHTTPStubsResponse responseWithData:stubData statusCode:200 headers:nil];
EXEMPLES
• Seulement les requêtes vers mywebservice.com!
• Utilisation d’un fichier « .json » (embarqué dans l’application)!
• Headers spécifiques
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {

!
!
}
!
!

return [request.URL.host isEqualToString:@"mywebservice.com"];
withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {

}];

return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"wsresponse.json",nil)
statusCode:200 headers:@{@"Content-Type":@"text/json"}];
EXEMPLES
• Seulement les requêtes vers mywebservice.com!
• Utilisation d’un NSDictionary converti automatiquement en JSON!
• Simulation d’un temps de réponse
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {

!
!
}
!
!

return [request.URL.host isEqualToString:@"mywebservice.com"];
withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
NSArray* stringList = [request.URL pathComponents];
NSDictionary* dico = @{@"response": stringList, @"error":@NO};
return [[OHHTTPStubsResponse responseWithJSONObject:dico statusCode:200 headers:nil]
responseTime:OHHTTPStubsDownloadSpeed3G];

}];

Catégorie OHHTTPStubsResponse+JSON.h!
Sérialise le dictionnaire en JSON + rajoute le header «application/json»
EXEMPLES
@implementation MyWebService
+ (NSString*)baseURL { return @"http://myserver.tld/webservice/"; }

!

+ (void)fetchUserWithID:(int)userID completion:(void(^)(id user))completion
{
NSURL* baseURL = [NSURL URLWithString:self.baseURL];
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];

}
@end

[sessionManager GET:[NSString stringWithFormat:@"user/%d",userID]
parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
completion(responseObject);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
completion(nil);
}];
EXEMPLES
@implementation MyWebService
+ (NSString*)baseURL { return @"http://myserver.tld/webservice/"; }

!

+ (void)fetchUserWithID:(int)userID completion:(void(^)(id user))completion
{
NSURL* baseURL = [NSURL URLWithString:self.baseURL];
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];

{

[sessionManager GET:[NSString stringWithFormat:@"user/%d",userID]
parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
completion(responseObject);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
completion(nil);
}];
(void)testFetchUser
}
@end

NSDictionary *expectedUser = @{@"id":@123,@"firstname":@"John",@"lastname":@"Appleseed"};
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.absoluteString hasPrefix:[MyWebService baseURL]];
} withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
return [OHHTTPStubsResponse responseWithJSONObject:expectedUser statusCode:200 headers:nil];
}];
[MyWebService fetchUserWithID:123 completion:^(id user) {
STAssertEqualObjects(user, expectedUser, @"Unexpected data received");
[self notifyAsyncOperationDone];
}];

}

[self waitForAsyncOperationWithTimeout:1.0];
INSTALLATION
• Sur

GITHub : http://github.com/AliSoftware/OHHTTPStubs!

• Disponible

via CocoaPods!

pod "OHHTTPStubs"

• Fonctionne

avec tout framework réseau utilisant l’URL Loading
System standard de Cocoa!
NSURLConnection, NSURLSession, AFNetworking, …!

• Limitations

:!

• Ne

supporte pas les Background Sessions!

• Ne

bouchonne pas l’upload de données

CocoaHeads Rennes #16: OHHTTPStubs

  • 1.
    OHHTTPSTUBS Bouchonner vos requêtesréseau facilement! sans modifier le code de votre application CocoaHeads Rennes! 20 février 2014 SmallTalk par O.Halligon
  • 2.
    OBJECTIF • Intercepter • Retourner lesrequêtes réseau sortantes! une réponse toute faite à la place! • La requête ne part finalement pas sur le net! • On se substitue au serveur pour retourner la réponse qu’on veut
  • 3.
    CAS D’USAGE • Tests Unitaires • • • Sansdépendre du réseau ou de la connectivité! Avec des réponses déterministes et invariantes. Tests d’asynchronisme en conditions dégradées • • • Simuler un réseau lent! Vérifier que votre application ne gèle pas et affiche des spinners Développement avant la livraison des BackEnds / WS • Développez vos modules d’appel aux WebServices
 même si ceux-ci ne sont pas encore prêts
  • 4.
    EXEMPLES • Toutes lesrequêtes retournent un texte constant [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { ! ! } ! ! return YES; withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { }]; NSData* stubData = [@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding]; return [OHHTTPStubsResponse responseWithData:stubData statusCode:200 headers:nil];
  • 5.
    EXEMPLES • Seulement lesrequêtes vers mywebservice.com! • Utilisation d’un fichier « .json » (embarqué dans l’application)! • Headers spécifiques [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { ! ! } ! ! return [request.URL.host isEqualToString:@"mywebservice.com"]; withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { }]; return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"wsresponse.json",nil) statusCode:200 headers:@{@"Content-Type":@"text/json"}];
  • 6.
    EXEMPLES • Seulement lesrequêtes vers mywebservice.com! • Utilisation d’un NSDictionary converti automatiquement en JSON! • Simulation d’un temps de réponse [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { ! ! } ! ! return [request.URL.host isEqualToString:@"mywebservice.com"]; withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { NSArray* stringList = [request.URL pathComponents]; NSDictionary* dico = @{@"response": stringList, @"error":@NO}; return [[OHHTTPStubsResponse responseWithJSONObject:dico statusCode:200 headers:nil] responseTime:OHHTTPStubsDownloadSpeed3G]; }]; Catégorie OHHTTPStubsResponse+JSON.h! Sérialise le dictionnaire en JSON + rajoute le header «application/json»
  • 7.
    EXEMPLES @implementation MyWebService + (NSString*)baseURL{ return @"http://myserver.tld/webservice/"; } ! + (void)fetchUserWithID:(int)userID completion:(void(^)(id user))completion { NSURL* baseURL = [NSURL URLWithString:self.baseURL]; AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL]; } @end [sessionManager GET:[NSString stringWithFormat:@"user/%d",userID] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { completion(responseObject); } failure:^(NSURLSessionDataTask *task, NSError *error) { completion(nil); }];
  • 8.
    EXEMPLES @implementation MyWebService + (NSString*)baseURL{ return @"http://myserver.tld/webservice/"; } ! + (void)fetchUserWithID:(int)userID completion:(void(^)(id user))completion { NSURL* baseURL = [NSURL URLWithString:self.baseURL]; AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL]; { [sessionManager GET:[NSString stringWithFormat:@"user/%d",userID] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { completion(responseObject); } failure:^(NSURLSessionDataTask *task, NSError *error) { completion(nil); }]; (void)testFetchUser } @end NSDictionary *expectedUser = @{@"id":@123,@"firstname":@"John",@"lastname":@"Appleseed"}; [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { return [request.URL.absoluteString hasPrefix:[MyWebService baseURL]]; } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { return [OHHTTPStubsResponse responseWithJSONObject:expectedUser statusCode:200 headers:nil]; }]; [MyWebService fetchUserWithID:123 completion:^(id user) { STAssertEqualObjects(user, expectedUser, @"Unexpected data received"); [self notifyAsyncOperationDone]; }]; } [self waitForAsyncOperationWithTimeout:1.0];
  • 9.
    INSTALLATION • Sur GITHub :http://github.com/AliSoftware/OHHTTPStubs! • Disponible via CocoaPods! pod "OHHTTPStubs" • Fonctionne avec tout framework réseau utilisant l’URL Loading System standard de Cocoa! NSURLConnection, NSURLSession, AFNetworking, …! • Limitations :! • Ne supporte pas les Background Sessions! • Ne bouchonne pas l’upload de données