38

iPhone Objective-C ユニット テストで、NSUrlConnection の +sendSynchronousRequest:returningResponse:error: メソッドなどのクラス メソッドをスタブ化したいことがよくあります。

簡単な例:

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

これを実行すると、次のようになります。

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

これに関するドキュメントを見つけるのに非常に苦労しましたが、OCMock ではクラス メソッドがサポートされていないと思います。

たくさんのグーグル検索の末、このヒントを見つけました。動作しますが、非常に面倒です: http://thom.org.uk/2009/05/09/mocking-class-methods-in-objective-c/

OCMock内でこれを行う方法はありますか? または、誰かがこの種のことを達成するために書かれた巧妙な OCMock カテゴリ オブジェクトを思いつくことができますか?

4

5 に答える 5

46

OCMock 3 の更新

OCMock は、クラス メソッドのスタブ化をサポートするために構文を最新化しました。

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

アップデート

OCMock は、標準でクラス メソッドのスタブ化をサポートするようになりました。OPのコードは、投稿されたとおりに機能するはずです。クラス メソッドと同じ名前のインスタンス メソッドがある場合、構文は次のようになります。

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

OCMock の機能を参照してください。

元の回答

Barry Wark の回答に続くサンプル コード。

connectionWithRequest:delegate: をスタブ化するだけの偽のクラス

@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

モックとの切り替え:

{
    ...
    // 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);
}
于 2012-12-05T00:11:32.457 に答える
18

Ruby の世界から来た私は、あなたが達成しようとしていることを正確に理解しています。どうやら、あなたは今日まったく同じことをしようとしている私より文字通り 3 時間先を行っていたようです (タイムゾーンのこと? :-)。

とにかく、これはOCMockで望む方法ではサポートされていないと思います。クラスメソッドをスタブ化するには、文字通りクラスに到達し、いつ、どこで、誰がメソッドを呼び出すかに関係なく、メソッドの実装を変更する必要があるためです。これは、指定されたクラスの「実際の」オブジェクトの代わりに、直接操作または操作するプロキシ オブジェクトを提供する OCMock の機能とは対照的です。

たとえば、NSURLConnection +sendSynchronousRequest:returningResponse:error: メソッドをスタブ化するのは理にかなっているようです。ただし、コード内でのこの呼び出しの使用はやや埋もれていることが一般的であるため、それをパラメーター化して NSURLConnection クラスのモック オブジェクトに交換するのは非常に厄介です。

このため、あなたが発見した「メソッドのスウィズリング」アプローチは、魅力的ではありませんが、クラスメソッドをスタブ化するためにまさにあなたがやりたいことだと思います。非常に扱いにくいと言うのは極端に思えます。「洗練されていない」ものであり、OCMock が私たちに命を吹き込むほど便利ではないことに同意するのはどうでしょうか。それにもかかわらず、それは問題に対するかなり簡潔な解決策です。

于 2009-11-28T00:00:45.507 に答える
6

これは、クラスメソッドのスウィズル実装を備えた優れた「要点」です:https ://gist.github.com/314009

于 2011-02-21T02:27:57.193 に答える
4

のクラスを注入するパラメータを取るようにテスト対象のメソッドを変更する場合、指定さNSURLConnectionれたセレクタに応答するモックを渡すのは比較的簡単です (セレクタを持つテスト モジュールでダミー クラスを作成する必要がある場合があります)。インスタンスメソッドとして作成し、そのクラスをモックします)。このインジェクションがなければ、本質的にNSURLConnection(クラス) をシングルトンとして使用するクラス メソッドを使用しているため、シングルトン オブジェクトを使用するアンチパターンに陥り、コードのテスト容易性が低下します。

于 2009-11-28T00:49:12.303 に答える
2

質問のブログ投稿へのリンクと RefuX の要点は、彼らのアイデアのブロック対応実装を思いつくように私に促しました: https://gist.github.com/1038034

于 2011-06-21T15:08:54.080 に答える