Как выполнить юнит-тестирование AFNetworking

Я делаю GET запрос на получение JSON данные с 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 но не могу понять, как справиться с этим.

1 ответ

Решение

Несколько вещей, чтобы прокомментировать ваш вопрос. Прежде всего, ваш код сложно протестировать, поскольку он напрямую создает AFHTTPClient. Я не знаю, так ли это, потому что это просто образец, но вы должны вместо этого ввести его (см. Образец ниже).

Во-вторых, вы создаете запрос, затем AFHTTPRequestOperation, а затем ставите его в очередь. Это нормально, но вы можете получить то же самое, используя метод AFHTTPClient getPath: параметры: успех: сбой:.

У меня нет опыта работы с этим предложенным инструментом HTTP-заглушки (Nocilla), но я вижу, что он основан на NSURLProtocol. Я знаю, что некоторые люди используют этот подход, но я предпочитаю создавать свои собственные объекты ответов с заглушками и издеваться над http-клиентом, как показано в следующем коде.

Retriever - это класс, который мы хотим проверить, где мы внедряем AFHTTPClient. Обратите внимание, что я передаю непосредственно идентификатор пользователя и события, поскольку хочу, чтобы все было просто и легко тестировать. Затем в другом месте вы передадите значение uid accout этому методу и так далее... Заголовочный файл будет выглядеть примерно так:

#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, поэтому рассмотрите возможность ее использования, если она соответствует вашим требованиям.

Другие вопросы по тегам