Frage

Oft finde ich in meinem iPhone Objective-C Unit-Tests, die ich eine Klassenmethode wollen Stummel, z.B. NSURLConnection der + sendSynchronousRequest: returningResponse: Fehler:. Methode

Vereinfachtes Beispiel:

- (void)testClassMock
{
    id mock = [OCMockObject mockForClass:[NSURLConnection class]];
    [[[mock stub] andReturn:nil] sendSynchronousRequest:nil returningResponse:nil error:nil];
}

Wenn diese ausgeführt wird, erhalte ich:

Test Case '-[WorklistTest testClassMock]' started.
Unknown.m:0: error: -[WorklistTest testClassMock] : *** -[NSProxy doesNotRecognizeSelector:sendSynchronousRequest:returningResponse:error:] called!
Test Case '-[WorklistTest testClassMock]' failed (0.000 seconds).

Ich habe eine wirklich harte Zeit habe, alle Unterlagen zu diesem Thema zu finden, aber ich nehme an, dass Klassenmethoden nicht von OCMock unterstützt werden.

Ich fand diese Spitze nach vielen googeln. Es funktioniert, aber es ist sehr umständlich: http://thom.org.uk/ 2009/05/09 / spöttisch-Klasse-Methoden-in-Ziel-c /

Gibt es trotzdem, dies innerhalb OCMock zu tun? Oder kann jemand denken Sie an einem cleveren OCMock Kategorie Objekt, das geschrieben werden könnte diese Art der Sache zu tun?

War es hilfreich?

Lösung

Aus der Welt von Ruby, ich verstehe genau, was Sie erreichen wollen. Offenbar waren Sie buchstäblich drei Stunden vor mir genau das Gleiche zu tun heute versuchen (Zeitzone Sache: -?).

Wie auch immer, I glaubt , dass diese nicht in der Art und Weise unterstützt wird, ein in OCMock wünschen würde, weil eine Klassenmethode stubbing muss buchstäblich in die Klasse erreichen und ändert seine Methode Implementierung unabhängig davon, wann, wo, oder wer ruft die Methode. Dies steht im Gegensatz zu dem, was OCMock scheint zu tun, die Ihnen ein Proxy-Objekt zur Verfügung zu stellen, die Sie manipulieren und sonstwie auf direkt und anstelle einem „echten“ Objekt der angegebenen Klasse.

Zum Beispiel scheint es sinnvoll NSURLConnection + sendSynchronousRequest zu wollen Stummel: returningResponse: Fehler: Methode. Es ist jedoch typisch, dass die Verwendung dieses Aufrufs in unserem Code etwas vergraben ist, wodurch es sehr umständlich es und Swap in einem Mock-Objekt für die NSURLConnection Klasse zu parametrieren.

Aus diesem Grund denke ich, dass die „Methode Swizzling“ -Ansatz Sie entdeckt haben, die zwar nicht sexy, genau das, was Sie für stubbing Klassenmethoden machen wollen. Zu sagen, es ist sehr beschwerlich scheint extrem - wie etwa wir es „unelegant“ zustimmen und vielleicht nicht so bequem wie OCMock Leben macht für uns. Trotzdem ist es eine ziemlich präzise Lösung für das Problem.

Andere Tipps

Update für OCMock 3

OCMock hat seine Syntax unterstützt Klassenmethode Anstoßen modernisiert:

id classMock = OCMClassMock([SomeClass class]);
OCMStub(ClassMethod([classMock aMethod])).andReturn(aValue);

Aktualisieren

OCMock unterstützt nun Klassenmethode aus der Box Anstoßen. Der Code des OP soll nun als gebucht arbeiten. Wenn es eine Instanz-Methode mit dem gleichen Namen wie die Klasse Methode ist, die Syntax ist:

[[[[mock stub] classMethod] andReturn:aValue] aMethod]

Siehe OCMock die Eigenschaften .

Original Antwort

Beispielcode folgende Barry Wark Antwort.

Die gefälschte Klasse, nur stubbing connectionWithRequest: Delegierter:

@interface FakeNSURLConnection : NSURLConnection
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
@end
@implementation FakeNSURLConnection
static id _sharedInstance;
+ (id)sharedInstance { if (!_sharedInstance) { _sharedInstance = [self init]; } return _sharedInstance; }
+ (void)setSharedInstance:(id)sharedInstance { _sharedInstance = sharedInstance; }
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate {
    return [FakeNSURLConnection.sharedInstance connectionWithRequest:request delegate:delegate];
}
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate { return nil; }
@end

Die Umstellung auf und von der Mock:

{
    ...
    // Create the mock and swap it in
    id nsurlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
    [FakeNSURLConnection setSharedInstance:nsurlConnectionMock];
    Method urlOriginalMethod = class_getClassMethod(NSURLConnection.class, @selector(connectionWithRequest:delegate:));
    Method urlNewMethod = class_getClassMethod(FakeNSURLConnection.class, @selector(connectionWithRequest:delegate:));
    method_exchangeImplementations(urlOriginalMethod, urlNewMethod);

    [[nsurlConnectionMock expect] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];

    ...
    // Make the call which will do the connectionWithRequest:delegate call
    ...

    // Verify
    [nsurlConnectionMock verify];

    // Unmock
    method_exchangeImplementations(urlNewMethod, urlOriginalMethod);
}

Hier ist ein schön ‚Kern‘ mit einem swizzle Implementierung für Klassenmethoden: https://gist.github.com / 314.009

Wenn Sie Ihre Methode im Test modifizieren, um einen Parameter zu nehmen, die die Klasse des NSURLConnection spritzt, dann ist es relativ einfach, in einem Mock zu übergeben, die auf den angegebenen Selektor reagiert (Sie müssen eine Dummy-Klasse im Testmodul erstellen können das hat den Selektor als Instanz Verfahren und mock dieser Klasse). Ohne diese Injektion, verwenden Sie eine Klassenmethode, im Wesentlichen NSURLConnection (die Klasse) als Singleton verwenden und somit in die anti-Muster gefallen Singleton Objekte verwenden und die Testbarkeit des Codes erlitten hat.

Link zum Blogpost in der Frage und RefuX Kern inspirierte mich mit Block aktivierte Umsetzung ihrer Ideen zu kommen: https : //gist.github.com/1038034

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top