Domanda

I'm trying to use unit testing to also test for networking. When called outside of my testing, the loginWithUserName:password: method properly fails and the outer failure block is called. When called from within my testing method, the failure block is never called (nor is the success block).

I think perhaps my semaphore wait is causing the networking to also wait, but I would't think so since it is on a different thread. I want it on a different thread so I can do an asynchronous call. Can I fix this to work properly? Should I use a different technique?

I have my test methods setup like this:

typedef void (^CompleteBlock)();

- (void)testLogin {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    [self callLoginWithCompletion:^{
        XCTAssertTrue(true, @"login Complete"); // expand on this when I get basic premise working
        NSLog(@"asserted true");
        dispatch_semaphore_signal(sema);
    }];

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

- (void)callLoginWithCompletion:(CompleteBlock)completeBlock {
    NSLog(@"login method called");
    [[NSNotificationCenter defaultCenter] addObserverForName:kLoginComplete
                                                      object:nil
                                                       queue:[NSOperationQueue mainQueue]
                                                  usingBlock:^(NSNotification *note) {
                                                      completeBlock();
                                                  }];

    [Network loginWithUserName:@"dummyUser" password:@"dummyPassword"];
}

And my login method looks like this: static AFNetworkReachabilityManager *_reachabilityManager;

+ (AFHTTPRequestOperationManager *)gatewayClient {
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        _gatewayClient = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:kGatewayBaseURL]];
        _gatewayClient.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/plain"];
    });

    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    securityPolicy.allowInvalidCertificates = YES;
    [AFHTTPRequestOperationManager manager].securityPolicy = securityPolicy;

    return _gatewayClient;
}

+ (NSString *)baseURLForType:(NSString *)type method:(NSString *)method {
    return [NSString stringWithFormat:@"api/%@/%@", type, method];
}

+ (void)loginWithUserName:(NSString *)userName password:(NSString *)password {
    [Network.gatewayClient
     GET:[self baseURLForType:@"auth"
                       method:@"getpubtoken"]
     parameters:nil
     success:^(AFHTTPRequestOperation *operation, id responseObject) {
         _authToken = responseObject;

         NSDictionary *parameters = @{
                                      @"UserId": userName
                                      , @"Password": password
                                      , kApiVersion: kApiVersion
                                      , kApiKey: kApiKeyValue
                                      , kAuthToken: _authToken
                                      , kFundraisingPlatform: @(Blackbaud)
                                      };

         [Network.gatewayClient
          POST:[self baseURLForType:@"auth"
                             method:@"loginfundraiser"]
          parameters:parameters
          success:^(AFHTTPRequestOperation *operation, id responseObject) {
              NSDictionary *responseDict = (NSDictionary *)responseObject;
              NSDictionary *userInfo = nil;

              _authToken = responseDict[kAuthToken];

              if ([responseDict[@"Successful"] boolValue]) {
                  userInfo = @{ kAuthToken: responseObject[kAuthToken] };
              } else {
                  userInfo = @{ @"error": [[NSError alloc] initWithDomain:@"Authorization"
                                                                     code:-1000
                                                                 userInfo:@{ @"message": responseDict[@"ExtendedMessages"] }] };
              }

              [[NSNotificationCenter defaultCenter] postNotificationName:kLoginComplete
                                                                  object:nil
                                                                userInfo:userInfo];
          }

          failure:^(AFHTTPRequestOperation *operation, NSError *error) {
              NSDictionary *userInfo = @{ @"error": error };
              [[NSNotificationCenter defaultCenter] postNotificationName:kLoginComplete
                                                                  object:nil
                                                                userInfo:userInfo];
          }];
     }

     failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSDictionary *userInfo = @{ @"error": error };
         [[NSNotificationCenter defaultCenter] postNotificationName:kLoginComplete
                                                             object:nil
                                                           userInfo:userInfo];
     }];
}
È stato utile?

Soluzione 2

Okay... the primary problem was the queues for AFNetworking AND the Notification were on the main thread. The semaphore is blocking the main thread, so both responses were blocked.

For the testing, a new NSOperationQueue had to be specified in place of the mainQueue. For the networking class, a new completionQueue had to be specified. There doesn't seem to be a way of setting a default completionQueue on these methods. Issue has been opened regarding this.

New test code and subset of the network shown below.

@implementation NetworkTests

NSOperationQueue * testOperationQueue;

- (void)setUp {
    [super setUp];
    testOperationQueue = [[NSOperationQueue alloc] init];
}

- (void)testLogin {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    [[NSNotificationCenter defaultCenter] addObserverForName:kLoginComplete
                                                      object:nil
                                                       queue:testOperationQueue
                                                  usingBlock:^(NSNotification *note) {
                                                      XCTAssertTrue(true, @"login Complete"); // expand on this when I get basic premise working
                                                      NSLog(@"asserted true");
                                                      dispatch_semaphore_signal(sema);
                                                  }];


    [Network loginWithUserName:@"testname" password:@"password"];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

@end

For the network, it has this:

static dispatch_queue_t completionQueue; // initialized when the _gatewayClient object is created

+ (void)loginWithUserName:(NSString *)userName password:(NSString *)password {
    AFHTTPRequestOperation *outerOperation =
    [Network.gatewayClient
     GET:[self baseURLForType:@"auth"
                       method:@"getpubtoken"]
     parameters:nil
     success:^(AFHTTPRequestOperation *operation, id responseObject) {
         // do stuff here
     }

     failure:^(AFHTTPRequestOperation *operation, NSError *error) {
         NSDictionary *userInfo = @{ @"error": error };
         [[NSNotificationCenter defaultCenter] postNotificationName:kLoginComplete
                                                             object:nil
                                                           userInfo:userInfo];
     }];

    outerOperation.completionQueue = completionQueue;
}

Altri suggerimenti

i know it is too late for answering.just for those coming to this thread afnetwork manager has a completion queue that its default is main queue . you can change it to another queue like background queue to avoid locking [AFHTTPSessionManager manager].completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top