2013-09-30 26 views
9

我想提出一个GET请求来检索JSON数据与AFNetworking如下面这段代码:如何单元测试AFNetworking要求

 NSURL *url = [NSURL URLWithString:K_THINKERBELL_SERVER_URL]; 
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url]; 
    Account *ac = [[Account alloc]init]; 
    NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:[NSString stringWithFormat:@"/user/%@/event/%@",ac.uid,eventID] parameters:nil]; 

    AFHTTPRequestOperation *operation = [httpClient HTTPRequestOperationWithRequest:request 
                      success:^(AFHTTPRequestOperation *operation, id responseObject) { 
                       NSError *error = nil; 
                       NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error]; 
                       if (error) { 
                       } 

                       [self.delegate NextMeetingFound:[[Meeting alloc]init] meetingData:JSON]; 

                      } 
                      failure:^(AFHTTPRequestOperation *operation, NSError *error){ 
                      }]; 
    [httpClient enqueueHTTPRequestOperation:operation]; 

的事情是我想根据这些数据来创建单元测试,但我不希望测试会实际发出请求。我想要一个预定义的结构将返回作为响应。我对单元测试是一种新鲜感,并探索了一点OCMock,但无法弄清楚如何管理它。

+0

你可能想看看[Nocilla](https://github.com/luisobo/Nocilla)。它使您可以轻松挂接到现有的NSURLConnections并定义它们的行为。 – mAu

回答

12

几个事情要评论你的问题。 首先,你的代码很难测试,因为它直接创建AFHTTPClient。我不知道是不是因为它只是一个样本,而是应该注入它(请参阅下面的示例)。

其次,你正在创建请求,然后AFHTTPRequestOperation,然后你入队。这很好,但您可以使用AFHTTPClient方法getPath:parameters:success:failure :.

我没有经验与建议的HTTP stubbing工具(Nocilla),但我看到它是基于NSURLProtocol。我知道有些人使用这种方法,但我更喜欢创建自己的存根响应对象并模拟http客户端,就像您在下面的代码中看到的那样。

Retriever是我们想要测试注入AFHTTPClient的类的类。 请注意,我直接传递用户和事件ID,因为我想让事情变得简单和容易测试。然后在其他地方,你会的户头UID值传递给该方法等等... 头文件将类似于此:

#import <Foundation/Foundation.h> 

@class AFHTTPClient; 
@protocol RetrieverDelegate; 

@interface Retriever : NSObject 

- (id)initWithHTTPClient:(AFHTTPClient *)httpClient; 

@property (readonly, strong, nonatomic) AFHTTPClient *httpClient; 

@property (weak, nonatomic) id<RetrieverDelegate> delegate; 

- (void) retrieveEventWithUserId:(NSString *)userId eventId:(NSString *)eventId; 

@end 


@protocol RetrieverDelegate <NSObject> 

- (void) retriever:(Retriever *)retriever didFindEvenData:(NSDictionary *)eventData; 

@end 

实现文件:

#import "Retriever.h" 
#import <AFNetworking/AFNetworking.h> 

@implementation Retriever 

- (id)initWithHTTPClient:(AFHTTPClient *)httpClient 
{ 
    NSParameterAssert(httpClient != nil); 

    self = [super init]; 
    if (self) 
    { 
     _httpClient = httpClient; 
    } 
    return self; 
} 

- (void)retrieveEventWithUserId:(NSString *)userId eventId:(NSString *)eventId 
{ 
    NSString *path = [NSString stringWithFormat:@"/user/%@/event/%@", userId, eventId]; 

    [_httpClient getPath:path 
       parameters:nil 
       success:^(AFHTTPRequestOperation *operation, id responseObject) 
    { 
     NSDictionary *eventData = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:NULL]; 
     if (eventData != nil) 
     { 
      [self.delegate retriever:self didFindEventData:eventData]; 
     } 
    } 
       failure:nil]; 
} 

@end 

而且测试:

#import <XCTest/XCTest.h> 
#import "Retriever.h" 

// Collaborators 
#import <AFNetworking/AFNetworking.h> 

// Test support 
#import <OCMock/OCMock.h> 

@interface RetrieverTests : XCTestCase 

@end 

@implementation RetrieverTests 

- (void)setUp 
{ 
    [super setUp]; 
    // Put setup code here; it will be run once, before the first test case. 
} 

- (void)tearDown 
{ 
    // Put teardown code here; it will be run once, after the last test case. 
    [super tearDown]; 
} 

- (void) test__retrieveEventWithUserIdEventId__when_the_request_and_the_JSON_parsing_succeed__it_calls_didFindEventData 
{ 
    // Creating the mocks and the retriever can be placed in the setUp method. 
    id mockHTTPClient = [OCMockObject mockForClass:[AFHTTPClient class]]; 

    Retriever *retriever = [[Retriever alloc] initWithHTTPClient:mockHTTPClient]; 

    id mockDelegate = [OCMockObject mockForProtocol:@protocol(RetrieverDelegate)]; 
    retriever.delegate = mockDelegate; 

    [[mockHTTPClient expect] getPath:@"/user/testUserId/event/testEventId" 
          parameters:nil 
          success:[OCMArg checkWithBlock:^BOOL(void (^successBlock)(AFHTTPRequestOperation *, id)) 
    { 
     // Here we capture the success block and execute it with a stubbed response. 
     NSString *jsonString = @"{\"some valid JSON\": \"some value\"}"; 
     NSData *responseObject = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; 

     [[mockDelegate expect] retriever:retriever didFindEventData:@{@"some valid JSON": @"some value"}]; 

     successBlock(nil, responseObject); 

     [mockDelegate verify]; 

     return YES; 
    }] 
          failure:OCMOCK_ANY]; 

    // Method to test 
    [retriever retrieveEventWithUserId:@"testUserId" eventId:@"testEventId"]; 

    [mockHTTPClient verify]; 
} 

@end 

最后要说的是,AFNetworking 2.0版本已经发布,因此如果它包含您的要求,请考虑使用它。