6

我有一个名为:ITunesAlbumDataDownloader 的网络类

@implementation AlbumDataDownloader

- (void)downloadDataWithURLString:(NSString *)urlString
                completionHandler:(void (^)(NSArray *, NSError *))completionHandler
{        
    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:urlString]
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {        
        NSArray *albumsArray = [self parseJSONData:data];

        completionHandler(albumsArray, error);
    }];

    [dataTask resume];
}


    - (NSArray *)parseJSONData:(NSData *)data {

        NSMutableArray *albums = [[NSMutableArray alloc] init];

...
...

        // Return the array
        return [NSArray arrayWithArray:albums];
    }

    @end

我需要为此创建一个单元测试,它执行以下操作:

  • NSURLSession dataTaskWithRequest:completionHandler: 响应被模拟为包含我拥有的假 JSON 数据:

// 预期的 JSON 响应

NSData *jsonResponse = [self sampleJSONData];
  • 公共方法 downloadDataWithURLString:completionHandler: 响应返回的数组应包含所有专辑和 nil 错误。

要记住的其他要点是,我需要使用伪造的 JSON 数据“jsonResponse”模拟 NSURLSession 到 downloadDataWithURLString:completionHandler: 方法而不调用实际的网络请求

我尝试了各种不同的事情,但我就是无法解决,我认为这是伪造请求和块的组合,这真的让我感到困惑。

这是我尝试过的测试方法的两个示例(实际上我也尝试了很多其他方法,但这是我现在剩下的):

- (void)testValidJSONResponseGivesAlbumsAndNilError {

    // Given a valid JSON response containing albums and an AlbumDataDownloaderTests instance

    // Expected JSON response
    NSData *jsonResponse = [self sampleJSONDataWithAlbums];

    id myMock = [OCMockObject mockForClass:[NSURLSession class]];

    [[myMock expect] dataTaskWithRequest:OCMOCK_ANY
                       completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {

    }];

    [myMock verify];
}

- (void)testValidJSONResponseGivesAlbumsAndNilError {

                // Given a valid JSON response containing albums and an AlbumDataDownloaderTests instance

                // Expected JSON response
                NSData *jsonResponse = [self sampleJSONDataWithAlbums];

            id myMock = [OCMockObject mockForClass:[AlbumDataDownloader class]];

            [[[myMock stub] andReturn:jsonResponse] downloadDataWithURLString:OCMOCK_ANY
                                                            completionHandler:^(NSArray *response, NSError *error)
            {

            }];

            [myMock verify];
            }
        }

我有一种感觉,在这两种情况下,我可能都离题了:(

我真的很感激这方面的一些帮助。

谢谢。

更新 1:

这是我现在想出的,但需要知道我是在正确的轨道上还是仍然犯了错误?

id mockSession = [OCMockObject mockForClass:[NSURLSession class]];
id mockDataTask = [OCMockObject mockForClass:[NSURLSessionDataTask class]];


[[mockSession stub] dataTaskWithRequest:OCMOCK_ANY
                        completionHandler:^(NSData  _Nullable data, NSURLResponse  Nullable response, NSError * Nullable error)
{
    NSLog(@"Response: %@", response);
}];


[[mockDataTask stub] andDo:^(NSInvocation *invocation)
{
    NSLog(@"invocation: %@", invocation);
}];
4

1 回答 1

3

使用块的技巧是你需要测试来调用块,无论测试需要什么参数。

在 OCMock 中,可以这样完成:

OCMStub([mock someMethodWithBlock:([OCMArg invokeBlockWithArgs:@"First arg", nil])]);

这很方便。但…


缺点是该块在被调用时会立即someMethodWithBlock:被调用。这通常不能反映生产代码的时间。

如果您想推迟调用该块,直到调用方法完成,然后捕获它。在 OCMock 中,可以这样完成:

__block void (^capturedBlock)(id arg1);
OCMStub([mock someMethodWithBlock:[OCMArg checkWithBlock:^BOOL(id obj) {
    capturedBlock = obj;
    return YES;
}]]);

// ...Invoke the method that calls someMethodWithBlock:, then...
capturedBlock(@"First arg"); // Call the block with whatever you need

我更喜欢使用OCHamcrest 的HCArgumentCaptor。OCMock 支持 OCHamcrest 匹配器,所以我相信这应该有效:

HCArgumentCaptor *argument = [[HCArgumentCaptor alloc] init];
OCMStub([mock someMethodWithBlock:argument]);

// ...Invoke the method that calls someMethodWithBlock:, then...
void (^capturedBlock)(id arg1) = argument.value; // Cast generic captured argument to specific type
capturedBlock(@"First arg"); // Call the block with whatever you need
于 2016-02-11T05:04:34.340 回答