单元测试完成&服务器调用的错误块

时间:2015-08-24 10:29:51

标签: ios objective-c unit-testing cocoa-touch xctest

我正在尝试为我班级中的一个方法编写单元测试。该方法进行服务器调用并通过完成&错误块。我知道如何从测试类进行异步服务器调用,但我怀疑的是如何测试使服务器调用完成的方法&错误块。这里的目的是检查是否完成&错误块正确处理服务器响应。

如果有人在此之前做过这个,请指导。如果我的理解不正确,请纠正我。

以下是一种此类方法的示例。在这里,我想编写一个可以测试“fetchItems”的单元测试。成功和错误处理案例的方法。

- (void)fetchItems {
    __weak MyListController *aBlockSelf = self;
    NSDictionary *aPostBody = @{kMyCategoryId : self.categoryId};

    // Fetch Items List From Server
    self.requestHandler = [[MyRequestHandler alloc] initWithEndPoint:[kMyFetchQuickListCategoryProductsURLKey  MyEndPoint] body:aPostBody container:self.view loadingOverlayTitle:nil successHandler:^(NSDictionary *iResponse) {
        if (iResponse) {
            aBlockSelf.isInfoData = [iResponse boolForKey:@"infoData"];
            aBlockSelf.isTestEnabled = [iResponse boolForKey:@"enabled"];
            NSArray *categoryData = iResponse[@"categoryData"];
            NSArray *itemList = categoryData[0][@"items"];

            NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:kMySortOrderKey ascending:YES];
            aBlockSelf.itemList = [MyListData quickListDataArray:[NSMutableArray arrayWithArray:[itemList sortedArrayUsingDescriptors:@[aSortDescriptor]]] withInventory:@10 andISInfoData: aBlockSelf.isInfoData];
        }

        [aBlockSelf fetchImages];
        [aBlockSelf refresh];
    } andErrorHandler:^(NSString *iMessage, NSString *iKey, NSInteger iErrorCode, BOOL iIsNetworkError) {
        MySessionObject.selectedCategory = nil;

        ([aBlockSelf.alertView isVisible]) {
            [aBlockSelf.alertView dismissWithClickedButtonIndex:self.alertView.cancelButtonIndex animated:YES];
        }

        aBlockSelf.alertView = nil;

        if (iIsNetworkError) {
            aBlockSelf.alertView = [[MyUIAlertView alloc] initWithTitle:@"Error" message:iMessage cancelButtonTitle:@"OK" cancelButtonActionHandler:^{
                [aBlockSelf dismissViewController];
            } otherButtonTitle:nil andOtherButtonActionHandler:nil];

            [aBlockSelf.alertView show];
        }

        aBlockSelf.navigationItem.rightBarButtonItem.enabled = YES;
        aBlockSelf.navigationItem.leftBarButtonItem.enabled = YES;
    }];

    [self.requestHandler executeRequest];
}

1 个答案:

答案 0 :(得分:0)

通常,如果您的服务器请求与模型混合在一起,则应将其与新对象或抽象接口分开。多亏了这一点,在测试时,您可以将服务器存根以发送固定响应。假设您已将请求分开发送到此对象:

@interface ServerCommuncation : NSObject

- (void)request:(id)request completion:(void (^)(NSError *error))completion;

@end

现在在您的单元测试中,您可以注入您的存根:

@interface ServerCommuncationStub : ServerCommunication

@property (nonatomic, copy) void (^completion)(NSError *error);

@end

@implementation ServerCommuncationStub

- (void)request:(id)request completion:(void (^)(NSError *error))completion {
    self.completion = completion;
}

并且只要适合您就致电完成:

ServerCommuncationStub *stub = [ServerCommuncationStub new];
Model *model = [[Model alloc] initWithServerCommunication:stub];
[model request];
stub.completion(nil);

更好的方法是使用模拟库。我个人使用OCMockito。没有子类化,您可以获得相同的效果:

ServerCommuncation *mock = mock([ServerCommuncation class]);
Model *model = [[Model alloc] initWithServerCommunication:mock];
[model request];

MKTArgumentCaptor *captor = [MKTArgumentCaptor new];
[verify(mock) request:anyting() completion:[captor capture]];
void(^block)(NSError *error) = [captor value];
block(nil);

您可以在RefactoringWorking Effectively with Legacy Code

中找到许多有关使代码可测试的有用信息