2013-03-25 14 views

Odpowiedz

0

Spróbuj KIWI ramy. Jest potężny i może Ci pomóc przy innych rodzajach testów.

27

Będziesz musiał obrócić pętlę, dopóki wywołanie zwrotne nie zostanie wywołane. Upewnij się jednak, że zostanie wywołany w głównej kolejce.

Spróbuj tego:

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

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

Można również użyć semafora (przykład poniżej), ale wolę kręcić runloop aby umożliwić asynchroniczne bloki wysyłane do głównej kolejki do przetworzenia.

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

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 
+0

thanx ..To naprawdę pomogło –

+1

dla tych, którzy mają problemy z podejściem pętli run: nie będzie działać poprawnie: the metoda 'runMode: beforeDate:' nie powróci, dopóki nie zostanie przetworzone zdarzenie źródłowe.To może się nigdy nie zdarzyć (chyba że test jednostkowy jawnie wykona to w instrukcji obsługi zakończenia);) – CouchDeveloper

+0

Użyłem kombinacji twojego rozwiązania z powiadomieniem, które jest już wywoływane na końcu mojej metody. Dzięki! –

1

Sugeruję, że powinieneś rzucić okiem na tests of Facebook-ios-sdk. To dobry przykład na to, jak przetestować test jednostek asynchronicznych na iOS, choć osobiście uważam, że testy asynchroniczne powinny zostać podzielone na testy synchronizacji.

FBTestBlocker: bloker, który zapobiega bieżącemu wątkowi z określonym limitem czasu. Możesz przeciągnąć i upuścić to do swojego projektu, ale musisz usunąć elementy związane z OCMock, jeśli nie masz tego w swoim projekcie.

FBTestBlocker.h

FBTestBlocker.m

FBURLConnectionTests: przykłady testowe należy patrzeć.

FBURLConnectionTests.h

FBURLConnectionTests.m

Ten fragment kodu powinien dać pewne wyobrażenie

- (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 jest makro C do pisania testów jednostkowych z operacji asynchronicznych i działa zarówno SenTestingKit i XCTest.

Proste i do punktu

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

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

    WAIT_WHILE(!jobDone, 2.0); 
} 
3

Oto kolejna alternatywa, XCAsyncTestCase, że dobrze współpracuje z OCMock jeśli chcesz go używać. Opiera się na testerze asynchronicznym GHUnit, ale zamiast niego używa standardowej struktury XCTest. W pełni kompatybilny z botami Xcode.

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

Wykorzystanie jest taka sama, tylko import i podklasa 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

Polecam Ci semafora połączenia + runloop, ja też napisał metodę, która podjęcia bloku:

// 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) 

metoda:

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

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

i nazywają

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

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

}); 
7

myślę wiele sugerowanych rozwiązań W tym poście występuje problem polegający na tym, że jeśli operacja asynchroniczna nie zakończy działania, flaga "done" nigdy nie zostanie ustawiona, a test zostanie zawieszony na zawsze.

Z powodzeniem stosowałem to podejście w wielu testach.

- (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

Oto Apple's description natywnego wsparcia dla testów asynchronicznym.

TL; DR instrukcja:

Spójrz na XCTextCase+AsynchronousTesting.h

Istnieje specjalna klasa XCTestExpectation z tylko jedną metodę publiczną: - (void)fulfill;

Należy Init wystąpienie tej klasy i sukcesu przypadku połączenia Metoda fulfill. W przeciwnym razie próba nie powiedzie się po limitu czasu, aby określić, w tej metodzie:

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

Przykład:

- (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 już dał right answer.

Po to, aby odpowiedź na pierwszy rzut oka wyglądała lepiej, wprowadzam tutaj przykładowy kod.

Użyj 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

można użyć asynchronicznego API wywołanie w szybkich jak to

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)") 
     } 
    } 
} 
Powiązane problemy