2013-03-25 37 views
14

Tôi đang gặp sự cố khi đơn vị kiểm tra cuộc gọi không đồng bộ trong iOS. (Mặc dù nó hoạt động tốt trong các bộ điều khiển xem.)Kiểm tra cuộc gọi không đồng bộ trong kiểm tra đơn vị trong iOS

Có ai từng gặp vấn đề này trước đây không? Tôi đã thử sử dụng một chức năng chờ đợi nhưng tôi vẫn phải đối mặt với cùng một vấn đề.

Vui lòng đề xuất ví dụ về cách hay để thực hiện việc này.

Trả lời

0

Hãy thử khuôn khổ KIWI. Nó mạnh mẽ và có thể giúp bạn với các loại thử nghiệm khác.

27

Bạn sẽ cần phải quay vòng chạy cho đến khi cuộc gọi lại của bạn được gọi. Hãy chắc chắn rằng nó được gọi trên hàng đợi chính, mặc dù.

Hãy thử điều này:

__block BOOL done = NO; 
doSomethingAsynchronouslyWithBlock(^{ 
    done = YES; 
}); 

while(!done) { 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
} 

Bạn cũng có thể sử dụng một semaphore (ví dụ dưới đây), nhưng tôi thích để quay runloop để cho phép các khối không đồng bộ cử đến hàng đợi chính để được xử lý.

dispatch_semaphore_t sem = dispatch_semaphore_create(0); 
doSomethingAsynchronouslyWithBlock(^{ 
    //... 
    dispatch_semaphore_signal(sem); 
}); 

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 
+0

thanx ..it thực sự giúp –

+1

Đối với những người không có vấn đề với cách tiếp cận chạy vòng lặp: nó sẽ không hoạt động chính xác: các phương thức 'runMode: beforeDate:' sẽ không trả về cho đến sau khi sự kiện của nguồn được xử lý.Điều đó có thể không bao giờ xảy ra (trừ khi kiểm tra đơn vị thực hiện rõ ràng rằng trong trình xử lý hoàn thành bằng cách nào đó);) – CouchDeveloper

+0

Tôi đã sử dụng kết hợp giải pháp của bạn với Thông báo đã được gọi khi kết thúc phương thức của tôi. Cảm ơn! –

1

Tôi khuyên bạn nên xem qua số tests of Facebook-ios-sdk. Đó là một ví dụ tốt về cách kiểm tra đơn vị kiểm tra async trên iOS, mặc dù cá nhân tôi nghĩ rằng các thử nghiệm không đồng bộ nên được chia thành các kiểm tra đồng bộ hóa.

FBTestBlocker: trình chặn ngăn chặn thoát khỏi chuỗi hiện tại với thời gian chờ được chỉ định. Bạn có thể kéo và thả nó vào dự án của bạn, nhưng bạn cần phải loại bỏ các công cụ liên quan đến OCMock nếu bạn không có nó trong dự án của bạn.

FBTestBlocker.h

FBTestBlocker.m

FBURLConnectionTests: ví dụ kiểm tra, bạn nên xem xét.

FBURLConnectionTests.h

FBURLConnectionTests.m

Đoạn mã này sẽ cho bạn một số ý tưởng

- (void)testExample 
{ 
    FBTestBlocker *_blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:1]; 
    __block BOOL excuted = NO; 
    [testcase test:^(BOOL testResult) { 
     XCTAssert(testResult, @"Should be true"); 
     excuted = YES; 
     [_blocker signal]; 
    }]; 

    [_blocker waitWithTimeout:4]; 
    XCTAssertTrue(excuted, @"Not executed"); 
} 
3

AGAsyncTestHelper là một macro C để viết bài kiểm tra đơn vị với các hoạt động không đồng bộ và làm việc với cả hai SenTestingKit và XCTest.

Đơn giản và đến điểm

- (void)testAsyncBlockCallback 
{ 
    __block BOOL jobDone = NO; 

    [Manager doSomeOperationOnDone:^(id data) { 
     jobDone = YES; 
    }]; 

    WAIT_WHILE(!jobDone, 2.0); 
} 
3

Đây là một thay thế, XCAsyncTestCase, hoạt động tốt với OCMock nếu bạn cần phải sử dụng nó. Nó dựa trên thử nghiệm không đồng bộ của GHUnit, nhưng thay vì sử dụng khung XCTest thông thường. Hoàn toàn tương thích với Xcode Bots.

https://github.com/iheartradio/xctest-additions

Cách sử dụng là như nhau, chỉ nhập khẩu và lớp con XCAsyncTestCase.

@implementation TestAsync 
- (void)testBlockSample 
{ 
    [self prepare]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){ 
     sleep(1.0); 
     [self notify:kXCTUnitWaitStatusSuccess]; 
    }); 
    // Will wait for 2 seconds before expecting the test to have status success 
    // Potential statuses are: 
    // kXCTUnitWaitStatusUnknown, initial status 
    // kXCTUnitWaitStatusSuccess, indicates a successful callback 
    // kXCTUnitWaitStatusFailure, indicates a failed callback, e.g login operation failed 
    // kXCTUnitWaitStatusCancelled, indicates the operation was cancelled 
    [self waitForStatus:kXCTUnitWaitStatusSuccess timeout:2.0]; 
} 
0

tôi khuyên bạn nên kết nối semaphore + runloop, tôi cũng đã viết phương pháp mà mất khối:

// Set the flag to stop the loop 
#define FLEND() dispatch_semaphore_signal(semaphore); 

// Wait and loop until flag is set 
#define FLWAIT() WAITWHILE(dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) 

// Macro - Wait for condition to be NO/false in blocks and asynchronous calls 
#define WAITWHILE(condition) \ 
do { \ 
while(condition) { \ 
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; \ 
} \ 
} while(0) 

phương pháp:

typedef void(^FLTestAsynchronousBlock)(void(^completion)(void)); 

void FLTestAsynchronous(FLTestAsynchronousBlock block) { 
    FLSTART(); 
    block(^{ 
     FLEND(); 
    }); 
    FLWAIT(); 
}; 

và gọi

FLTestAsynchronous(^(void(^completion)()){ 

    [networkManager signOutUser:^{ 
     expect(networkManager.currentUser).to.beNil(); 
     completion(); 
    } errorBlock:^(NSError *error) { 
     expect(networkManager.currentUser).to.beNil(); 
     completion(); 
    }]; 

}); 
7

Tôi nghĩ nhiều giải pháp được đề xuất i n bài đăng này có vấn đề là nếu hoạt động không đồng bộ không hoàn thành cờ "đã hoàn tất" chưa bao giờ được đặt và thử nghiệm sẽ bị treo vĩnh viễn.

Tôi đã sử dụng thành công phương pháp này trong nhiều thử nghiệm của mình.

- (void)testSomething { 
    __block BOOL done = NO; 

    [obj asyncMethodUnderTestWithCompletionBlock:^{ 
     done = YES; 
    }]; 

    XCTAssertTrue([self waitFor:&done timeout:2], 
        @"Timed out waiting for response asynch method completion"); 
} 


- (BOOL)waitFor:(BOOL *)flag timeout:(NSTimeInterval)timeoutSecs { 
    NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs]; 

    do { 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate]; 
     if ([timeoutDate timeIntervalSinceNow] < 0.0) { 
      break; 
     } 
    } 
    while (!*flag); 
    return *flag; 
} 
13

Đây là Apple's description hỗ trợ bản địa để thử nghiệm async.

TL; DR dẫn sử dụng:

Nhìn vào XCTextCase+AsynchronousTesting.h

Có đặc biệt lớp XCTestExpectation chỉ với một phương pháp công cộng: - (void)fulfill;

Bạn nên init thể hiện của lớp này và trong thành công trường hợp cuộc gọi fulfill. Nếu không thử nghiệm của bạn sẽ thất bại sau khi thời gian chờ mà bạn chỉ định trong phương pháp đó:

- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(XCWaitCompletionHandler)handlerOrNil; 

Ví dụ:

- (void)testAsyncMethod 
{ 

    //Expectation 
    XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method Works Correctly!"]; 

    [MyClass asyncMethodWithCompletionBlock:^(NSError *error) {   
     if(error) 
      NSLog(@"error is: %@", error); 
     else 
      [expectation fulfill]; 
    }]; 

    //Wait 1 second for fulfill method called, otherwise fail:  
    [self waitForExpectationsWithTimeout:1 handler:^(NSError *error) { 

     if(error) 
     { 
      XCTFail(@"Expectation Failed with error: %@", error); 
     } 

    }]; 
} 
2

Sam Brodkin đã đưa ra các right answer.

Chỉ để làm cho câu trả lời có vẻ tốt hơn ngay từ cái nhìn đầu tiên, tôi mang mã mẫu ở đây.

Sử dụng XCTestExpectation.

// Test that the document is opened. Because opening is asynchronous, 
// use XCTestCase's asynchronous APIs to wait until the document has 
// finished opening. 

- (void)testDocumentOpening 
{ 
    // Create an expectation object. 
    // This test only has one, but it's possible to wait on multiple expectations. 
    XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:@"document open"]; 

    NSURL *URL = [[NSBundle bundleForClass:[self class]] 
          URLForResource:@"TestDocument" withExtension:@"mydoc"]; 
    UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL]; 
    [doc openWithCompletionHandler:^(BOOL success) { 
     XCTAssert(success); 
     // Possibly assert other things here about the document after it has opened... 

     // Fulfill the expectation-this will cause -waitForExpectation 
     // to invoke its completion handler and then return. 
     [documentOpenExpectation fulfill]; 
    }]; 

    // The test will pause here, running the run loop, until the timeout is hit 
    // or all expectations are fulfilled. 
    [self waitForExpectationsWithTimeout:1 handler:^(NSError *error) { 
     [doc closeWithCompletionHandler:nil]; 
    }]; 
} 
0

bạn có thể sử dụng async api gọi trong nhanh chóng như thế này

private let serverCommunicationManager : ServerCommunicationManager = { 
    let instance = ServerCommunicationManager() 
    return instance 
}() 

var expectation:XCTestExpectation? 
func testAsyncApiCall() { 
    expectation = self.expectation(description: "async request") 

    let header = ["Authorization":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ4MmY1MTcxNzI4YTA5MjI3NWIzYWI3OWNkOTZjMGExOTI4MmM2NDEyZjMyYWQzM2ZjMzY4NmU2MjlhOWY2YWY1NGE0MDI4MmZiNzY2NWQ3In0.eyJhdWQiOiIxIiwianRpIjoiZDgyZjUxNzE3MjhhMDkyMjc1YjNhYjc5Y2Q5NmMwYTE5MjgyYzY0MTJmMzJhZDMzZmMzNjg2ZTYyOWE5ZjZhZjU0YTQwMjgyZmI3NjY1ZDciLCJpYXQiOjE1MDg4MjU1NTEsIm5iZiI6MTUwODgyNTU1MSwiZXhwIjoxNTQwMzYxNTUxLCJzdWIiOiIiLCJzY29wZXMiOltdfQ.osoMQgiY7TY7fFrh5r9JRQLQ6AZhIuEbrIvghF0VH4wmkqRUE6oZWjE5l0jx1ZpXsaYUhci6EDngnSTqs1tZwFTQ3srWxdXns2R1hRWUFkAN0ri32W0apywY6BrahdtiVZa9LQloD1VRMT1_QUnljMXKsLX36gXUsNGU6Bov689-bCbugK6RC3n4LjFRqJ3zD9gvkRaODuOQkqsNlS50b5tLm8AD5aIB4jYv3WQ4-1L74xXU0ZyBTAsLs8LOwvLB_2B9Qdm8XMP118h7A_ddLo9Cyw-WqiCZzeZPNcCvjymNK8cfli5_LZBOyjZT06v8mMqg3zszWzP6jOxuL9H1JjBF7WrPpz23m7dhEwa0a-t3q05tc1RQRUb16W1WhbRJi1ufdMa29uyhX8w_f4fmWdAnBeHZ960kjCss98FA73o0JP5F0GVsHbyCMO-0GOHxow3-BqyPOsmcDrI4ay006fd-TJk52Gol0GteDgdntvTMIrMCdG2jw8rfosV6BgoJAeRbqvvCpJ4OTj6DwQnV-diKoaHdQ8vHKe-4X7hbYn_Bdfl52gMdteb3_ielcVXIaHmQ-Dw3E2LSVt_cSt4tAHy3OCd7WORDY8uek4Paw8Pof0OiuqQ0EB40xX5hlYqZ7P_tXpm-W-8ucrIIxgpZb0uh-wC3EzBGPjpPD2j9CDo"] 
    serverCommunicationManager.sendServerRequest(httpMethodType: .get, baseURL: "http://192.168.2.132:8000/api/v1/user-role-by-company-id/2", param: nil, header: header) { (isSuccess, msg , response) in 
     if isSuccess 
     { 
      let array = response as! NSArray 

      if array.count == 8 
      { 
       XCTAssertTrue(true) 
       self.expectation?.fulfill() 
      } 
      else 
      { 
       XCTAssertFalse(false) 
       XCTFail("array count fail") 
      } 
     } 
    } 
    waitForExpectations(timeout: 5) { (error) in 
     if let error = error{ 
      XCTFail("waiting with error: \(error.localizedDescription)") 
     } 
    } 
} 
Các vấn đề liên quan