0

OCMock を使用してクラス メソッドが呼び出されているかどうかを確認しようとしています。OCMock の Web サイトや SO に関するその他の回答から、新しい OCMock リリース (2.1) がスタブ クラス メソッドのサポートを追加することを収集しました。

私は同じことをしようとしています:

詳細ビューコントローラー:

+(BOOL)getBoolVal
{
return YES;
}

テストケース:

-(void) testClassMethod
{
id detailMock = [OCMockObject mockForClass:[DetailViewController class]];

[[[detailMock stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})] getBoolVal:nil];
}

getBoolValテストは実行中で成功していますが、 のメソッドから YES ではなく NO を返しても成功しDetailViewControllerます。そのメソッドにブレークポイントを保持すると、テストの実行は停止せず、メソッドが呼び出されていないことを示します。

クラスメソッドを確認するにはどうすればよいですか?

4

2 に答える 2

0

編集

OCMock の最新バージョンを詳しく調べたところ、直接テストしているクラス メソッドをモックする方法を解釈するのは正しいと思います。問題は単に間違ったシグネチャで呼び出しているということですか?

以下の回答は一般的なケースです (テスト対象のメソッドがクラス メソッドを呼び出す場合にも役立ちます)。

オリジナル

OCMock でクラス メソッドをチェックするのは少し難しいです。あなたが現在行っていることは、detailMock というモックオブジェクトを作成し、 getBoolVal というインスタンスメソッドをスタブ化することです: Apple のガイドラインに従いたい場合は、ゲッターで「get」という単語を使用しないことをお勧めします (get set へのポインター参照を送信している場合を除きます)。detailMock は ID であり、任意のセレクターに応答する意思があるため、コンパイルは失敗しません。

では、Class メソッドをテストするにはどうすればよいでしょうか。一般的なケースでは、スウィズリングを行う必要があります。これが私のやり方です。

NSURLConnectionクラスにも適用できるように偽装する方法を見てみましょう。

クラスを拡張することから始めます。

@interface FakeNSURLConnection : NSURLConnection

+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
+ (void)enableMock:(id)mock;
+ (void)disableMock;

- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
@end

connectionWithRequest:delegate のテストに関心があり、クラスを拡張して、クラスメソッドと同じシグネチャを持つパブリックインスタンスメソッドを追加したことに注意してください。実装を見てみましょう。

@implementation FakeNSURLConnection

SHARED_INSTANCE_IMPL(FakeNSURLConnection);    
SWAP_METHODS_IMPL(NSURLConnection, FakeNSURLConnection);    
DISABLE_MOCK_IMPL(FakeNSURLConnection);    
ENABLE_MOCK_IMPL(FakeNSURLConnection);    

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

それで、ここで何が起こっているのですか?最初に、以下で説明するいくつかのマクロがあります。次に、クラス メソッドをオーバーライドして、インスタンス メソッドを呼び出すようにしました。を使用OCMockしてインスタンス メソッドをモックできるので、クラス メソッドにインスタンス メソッドを呼び出させることで、クラス メソッドにモックを呼び出させることができます。

ただし、実際のコードでは FakeNSURLConnection を使用したくありませんが、テストでは使用したいと考えています。どうすればこれを行うことができますか?と の間でクラス メソッドを入れ替えることができます。つまり、 callで呼び出しをスウィズルした後です。これにより、マクロが表示されます。NSURLConnectionFakeNSURLConnectionNSURLConnection connectionWithRequest:delegateFakeNSURLConnection connectionWithRequest:delegate

#define SWAP_METHODS_IMPL(REAL, FAKE) \
+ (void)swapMethods \
{ \
    Method original, mock; \
    unsigned int count; \
    Method *methodList = class_copyMethodList(object_getClass(REAL.class), &count); \
    for (int i = 0; i < count; i++) \
    { \
        original = class_getClassMethod(REAL.class, method_getName(methodList[i])); \
        mock = class_getClassMethod(FAKE.class, method_getName(methodList[i])); \
        method_exchangeImplementations(original, mock); \
    } \
    free(methodList); \
}

#define DISABLE_MOCK_IMPL(FAKE) \
+ (void)disableMock \
{ \
    if (_mockEnabled) \
    { \
        [FAKE swapMethods]; \
        _mockEnabled = NO; \
    } \
}

#define ENABLE_MOCK_IMPL(FAKE) \
static BOOL _mockEnabled = NO; \
+ (void)enableMock:(id)mockObject; \
{ \
    if (!_mockEnabled) \
    { \
        [FAKE setSharedInstance:mockObject]; \
        [FAKE swapMethods]; \
        _mockEnabled = YES; \
    } \
    else \
    { \
        [FAKE disableMock]; \
        [FAKE enableMock:mockObject]; \
    } \
}

#define SHARED_INSTANCE_IMPL() \
+ (id)sharedInstance \
{ \
    return _sharedInstance; \
}

#define SET_SHARED_INSTANCE_IMPL() \
+ (void)setSharedInstance:(id)sharedInstance \
{ \
    _sharedInstance = sharedInstance; \
}

クラスメソッドを誤って再スウィズルしないように、このようなことをお勧めします。では、これをどのように使用しますか?

id urlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
[FakeNSURLConnection enableMock:urlConnectionMock];
[_mocksToDisable addObject:FakeNSURLConnection.class];

[[[urlConnectionMock expect] andReturn:urlConnectionMock] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];

これで終わりです。偽のクラスが呼び出され、モックが呼び出されるように、メソッドを入れ替えました。

あ、でも最後にもう一つ。_mocksToDisable は、スウィズルNSMutableArrayしたすべてのクラスのクラス オブジェクトを含む です。

- (void)tearDown
{
    for (id mockToDisable in _mocksToDisable)
    {
        [mockToDisable disableMock];
    }
}

これはtearDown、テストの実行後にクラスのスウィズルを解除したことを確認するために行います。例外が発生した場合、すべてのテスト コードが実行されるわけではありませんが、tearDown は常に実行されるため、テストで正しく実行しないでください。

これを簡単にするモック テクノロジは他にもあるかもしれませんが、1 回記述すれば何度も使用できるため、それほど悪くはないことがわかりました。

于 2013-05-10T16:42:28.257 に答える