Wie kann ich HTTP-Anfragen und -Antworten mithilfe von NSURLSession in iOS 7.1 testen?
Ich würde gerne wissen, wie man "unit test" von HTTP-Anfragen und-Antworten mit NSURLSession. Jetzt meine Abschluss-block-code wird nicht aufgerufen, wenn die Ausführung als unit test. Jedoch, wenn der gleiche code ausgeführt wird, innerhalb der AppDelegate
(didFinishWithLaunchingOptions), wird der code innerhalb der completion-block wird aufgerufen. Wie bereits in diesem thread, NSURLSessionDataTask dataTaskWithURL Abschluss-handler nicht immer genanntdie Verwendung von Semaphoren und/oder dispatch_group ist nötig", um sicherzustellen, dass der Haupt-thread blockiert, bis die Netzwerk-Anfrage beendet ist."
Meine HTTP-post-code sieht wie folgt aus.
@interface LoginPost : NSObject
- (void) post;
@end
@implementation LoginPost
- (void) post
{
NSURLSessionConfiguration* conf = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:conf delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSURL* url = [NSURL URLWithString:@"http://www.example.com/login"];
NSString* params = @"[email protected]&password=test";
NSMutableRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Response:%@ %@\n", response, error); //code here never gets called in unit tests, break point here never is triggered as well
//response is actually deserialized to custom object, code omitted
}];
[task resume];
}
@end
Die Methode, die tests dieser sieht wie folgt aus.
- (void) testPost
{
LoginPost* loginPost = [LoginPost alloc];
[loginPost post];
//XCTest continues by operating assertions on deserialized HTTP response
//code omitted
}
In meinem AppDelegate
den Abschluss-block-code funktioniert, und es sieht wie folgt aus.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//generated code
LoginPost* loginPost = [LoginPost alloc];
[loginPost post];
}
Irgendwelche Hinweise, wie man den Abschluss-block ausgeführt, wenn die Ausführung in einem unit test? Ich bin ein Neuling in der iOS-also ein klares Beispiel würde wirklich helfen.
- Hinweis: ich weiß, was ich Fragen kann auch nicht sein "unit" - Tests in einem strengen Sinn, ich verlasse mich auf einen HTTP-server als Teil meiner Prüfung (also, was ich fordere, ist mehr wie eine integration testing).
- Hinweis: ich weiß es gibt einen anderen thread Unit-tests mit NSURLSession über unit-testing mit NSURLSession, aber ich will nicht spotten Antworten.
InformationsquelleAutor der Frage Jane Wayne | 2014-05-14
Du musst angemeldet sein, um einen Kommentar abzugeben.
Xcode 6 kann jetzt mit asynchronen tests mit
XCTestExpectation
. Wenn bei der Prüfung ein asynchroner Prozess, stellen Sie die "Erwartung", dass dieser Prozess abgeschlossen wird, asynchron, nach der Ausgabe des asynchronen Prozesses warte, bis die Erwartung erfüllt sein, damit eine Feste Menge an Zeit, und wenn die Abfrage abgeschlossen ist, werden Sie asynchron erfüllen die Erwartung.Beispiel:
Meine Vorherige Antwort, unten, älter als
XCTestExpectation
aber ich halte es für historische Zwecke.Weil der test läuft auf dem Haupt-Warteschlange, und weil Ihre Anfrage asynchron ausgeführt wird, ist Ihr test nicht zu erfassen die Ereignisse in der Abschluss-block. Die Verwendung einer semaphore oder Versand-Gruppe um die Anfrage synchron.
Beispiel:
Semaphore wird sichergestellt, dass der test nicht abgeschlossen, bis die Anfrage stellt.
Klar, meine obige test ist nur eine zufällige HTTP-request, aber ich hoffe es verdeutlicht die Idee. Und meine verschiedenen
XCTAssert
Aussagen identifizieren vier Arten von Fehlern:Den
NSError
Objekt nicht null.Den HTTP-Statuscode nicht 200 ist.
Den
NSData
Objekt war gleich null.Den Abschluss-block nicht innerhalb von 60 Sekunden.
Würden Sie vermutlich auch hinzufügen, tests für den Inhalt der Antwort (was ich nicht tun in diesem vereinfachten Beispiel).
Beachten Sie den oben genannten test funktioniert, weil es gibt nichts in meinem Abschluss-block, der Versand nichts für die main queue. Wenn Sie testen dies mit einer asynchronen operation, die erfordert, dass die main queue (was wird passieren, wenn Sie nicht vorsichtig mit AFNetworking oder manuell den Versand an die main queue selbst), deadlocks können mit dem obigen Muster (weil wir die Blockierung der main-thread wartet für die Netzwerk-Anfrage zu beenden). Aber im Fall von
NSURLSession
dieses Muster funktioniert Super.Fragte Sie über das tun Tests von der Kommandozeile aus, unabhängig vom simulator. Es gibt ein paar Aspekte:
Wenn Sie testen wollen, aus der Befehlszeile, die Sie verwenden können
xcodebuild
von der Befehlszeile aus. Zum Beispiel, um zu testen, auf einem simulator von der Kommandozeile aus, wäre es (in meinem Beispiel mein Schema den NamenNetworkTest
):Bauen das Programm und führen Sie es auf das angegebene Ziel. Beachten Sie, es gibt viele Berichte von Xcode 5.1 Probleme testen von apps auf dem simulator von der Kommandozeile aus mit
xcodebuild
(und ich kann dieses Verhalten überprüfen, weil ich eine Maschine, die das oben funktioniert gut, aber es friert auf einem anderen). Befehlszeile testen gegen-simulator in Xcode 5.1 scheint nicht ganz zuverlässig.Wenn Sie nicht möchten, dass Ihre test-Ausführung auf dem simulator (und dies gilt, ob es von der Kommandozeile oder aus Xcode), können Sie bauen ein MacOS-X-target, und eine zugehörige Regelung für das bauen. Zum Beispiel, ich habe einen Mac OS X-Ziel, um meine app und dann noch ein System namens
NetworkTestMacOS
.BTW, wenn Sie fügen Sie eine Mac OS X-Schema und existierenden iOS-Projekt werden die tests möglicherweise nicht automatisch auf das Schema, so haben Sie vielleicht zu tun, die manuell durch Bearbeiten der Regelung, navigieren Sie zu den tests Abschnitt, und fügen Sie Ihre test-Klasse gibt. Anschließend können Sie die Mac OS X-tests in Xcode, indem Sie die rechts-Schema, oder Sie können es von der Befehlszeile zu:
Beachten Sie auch, wenn Sie bereits Ihr Ziel, können Sie die tests direkt durch die Navigation auf der rechten
DerivedData
Ordner (in meinem Beispiel ist es~/Library/Developer/Xcode/DerivedData/NetworkTest-xxx/Build/Products/Debug
) und dann läuftxctest
direkt von der Befehlszeile aus:Weitere Möglichkeit zur Isolation der tests Ihre Xcode-Sitzung zu tun, Ihre Prüfung auf einem separaten OS X Server. Finden Sie die Kontinuierliche Integration und Tests Abschnitt der WWDC 2013 video Tests in Xcode. Zu gehen, dass hier weit über den Rahmen der ursprünglichen Frage, also werde ich einfach verweisen Sie auf das video, das gibt eine schöne Einführung in das Thema.
Persönlich, ich mag die Tests integration in Xcode (er macht das Debuggen von tests unendlich viel leichter) und ein Mac OS X-Ziel, Sie zu umgehen den simulator in diesem Prozess. Aber wenn Sie wollen, um es von der Befehlszeile aus (oder Mac OS X Server), vielleicht ist die oben hilft.
InformationsquelleAutor der Antwort Rob