Как написать модульный тест для метода с обработчиком завершения, который возвращает данные? - PullRequest
0 голосов
/ 19 мая 2019

Я застрял, пытаясь понять, как написать модульный тест для такого метода, используя OCMock. Может ли кто-нибудь помочь мне с этим?

- (void)executeRequest:(NSURLRequest *)request withCompletionHandler:(void (^)(id responseData, NSError *error))completionHandler
{
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error)
        {
            NSLog(@"Network error occurred: %@", [error localizedDescription]);
            dispatch_async(dispatch_get_main_queue(), ^(void) { completionHandler(nil, error);
            });
            return;
        }
    if ([response isKindOfClass:NSHTTPURLResponse.class])
    {
        NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
        if (statusCode != 200)
        {
            NSLog(@"Network error occurred. Status code: %ld", (long)statusCode);
            return;
        }
    }
        completionHandler(data, nil);
    }];
    [dataTask resume];
}

1 Ответ

2 голосов
/ 19 мая 2019

Вы должны использовать OCMArg с checkWithBlock или invokeBlockWithArgs для проверки обработчиков завершений.Вот пример:

  1. Создание подкласса XCTestCase с помощью команды щелчка + N:

New_file

Добавить свойства для тестируемого экземпляра и необходимые макеты:

@interface SomeClassTests : XCTestCase

@property (nonatomic, strong) SomeClass *testableInstance;
@property (nonatomic, strong) NSURLSession *mockSession;
@property (nonatomic, strong) NSURLRequest *mockRequest;
@property (nonatomic, strong) NSHTTPURLResponse *mockResponse;

@end 

Свойства настройки:

- (void)setUp
{
    [super setUp];
    self.testableInstance = [SomeClass new];
    self.mockSession = OCMClassMock([NSURLSession class]);
    self.mockRequest = OCMClassMock([NSURLRequest class]);
    self.mockResponse = OCMClassMock([NSHTTPURLResponse class]);
    OCMStub(ClassMethod([(id)self.mockSession sharedSession])).andReturn(self.mockSession);
}

Не 'не забудьте очистить при разборке:

- (void)tearDown
{
    [(id)self.mockSession stopMocking];
    self.mockResponse = nil;
    self.mockRequest = nil;
    self.mockSession = nil;
    self.testableInstance = nil;
    [super tearDown];
}

Давайте проверим случай, когда происходит ошибка:

- (void)testWhenErrorOccuersThenCompletionWithSameError
{
    // arrange
    NSError *givenError = [[NSError alloc] initWithDomain:@"Domain" code:0 userInfo:nil];
    OCMStub([self.mockSession dataTaskWithRequest:[OCMArg any] completionHandler:([OCMArg invokeBlockWithArgs:@"", self.mockResponse, givenError, nil])]);

    void (^givenCompletion)(id  _Nonnull, NSError * _Nonnull) = ^void(id  _Nonnull responseData, NSError * _Nonnull resultError) {
        // assert
        XCTAssertNil(responseData);
        XCTAssertEqual(resultError, givenError);
    };

    // act
    [self.testableInstance executeRequest:self.mockRequest withCompletionHandler:givenCompletion];
}

Итакмы будем уверены, что если произойдет какая-либо ошибка, обработчик завершения будет вызываться с той же ошибкой в ​​аргументе.

Давайте проверим, когда мы получим какой-то плохой код состояния:

- (void)testWhenBadStatusCodeThenReturnWithoutCompletion
{
    // arrange
    OCMStub([self.mockResponse statusCode]).andReturn(403);
    OCMStub([self.mockSession dataTaskWithRequest:[OCMArg any] completionHandler:([OCMArg checkWithBlock:^BOOL(id param) {
        void (^passedCompletion)(NSData *data, NSURLResponse *response, NSError *error) = param;
        passedCompletion(nil, self.mockResponse, nil);
        return YES;
    }])]);

    void (^givenCompletion)(id  _Nonnull, NSError * _Nonnull) = ^void(id  _Nonnull responseData, NSError * _Nonnull resultError) {
        // assert
        XCTFail("Shouldn't be reached");
    };

    // act
    [self.testableInstance executeRequest:self.mockRequest withCompletionHandler:givenCompletion];
}

И, наконец, давайте проверим, когда мы действительно получим данные:

- (void)testWhenSuccesThenCompletionWithSameData
{
    // arrange
    NSData *givenData = [NSData data];
    OCMStub([self.mockResponse statusCode]).andReturn(200);
    OCMStub([self.mockSession dataTaskWithRequest:[OCMArg any] completionHandler:([OCMArg checkWithBlock:^BOOL(id param) {
        void (^passedCompletion)(NSData *data, NSURLResponse *response, NSError *error) = param;
        passedCompletion(givenData, self.mockResponse, nil);
        return YES;
    }])]);

    void (^givenCompletion)(id  _Nonnull, NSError * _Nonnull) = ^void(id  _Nonnull responseData, NSError * _Nonnull resultError) {
        // assert
        XCTAssertEqual(responseData, givenData);
    };

    // act
    [self.testableInstance executeRequest:self.mockRequest withCompletionHandler:givenCompletion];
}

Если вы включите покрытие, то увидите, что такой тест полностью покрывает тестируемый код:

Coverage

...