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

Posted

技术标签:

【中文标题】服务器调用的完成和错误块的单元测试【英文标题】:Unit tests for completion & error blocks for server calls 【发布时间】:2015-08-24 10:29:51 【问题描述】:

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

如果有人以前这样做过,请指导。如果我的理解不正确,请纠正我。

以下是一种此类方法的示例。在这里,我想编写一个单元测试,它可以测试 '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];

【问题讨论】:

一些示例代码会很有用。 @TomaszBąk:感谢您的回复。我仍然对确切的实施有疑问。我添加了一个示例方法,我正在尝试为其编写测试用例。请您参考这个方法解释一下。 【参考方案1】:

一般来说,如果你的模型中有一个服务器请求,你应该用一个新的对象或抽象接口将它分开。多亏了这一点,在测试时,您可以存根服务器以发送固定响应。假设您已将请求分开发送到此对象:

@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);

您可以在Refactoring 和Working Effectively with Legacy Code 中找到许多关于使您的代码可测试的有用信息

【讨论】:

以上是关于服务器调用的完成和错误块的单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何在单元测试之前等待组件的挂载回调中的异步调用完成?

XCode 5.1 单元测试覆盖分析在使用块的文件上失败

微服务下的单元测试和集成测试

mockito单元测试

单元测试错误:无法从同步测试中调用 Promise.then

如何使用不同的方法多次模拟调用 AWS 服务的 Golang 函数的单元测试?