Skip to content

Instantly share code, notes, and snippets.

@t-oginogin
Last active December 15, 2015 11:09
Show Gist options
  • Save t-oginogin/5251066 to your computer and use it in GitHub Desktop.
Save t-oginogin/5251066 to your computer and use it in GitHub Desktop.

OCMockを使う

OCMockのインストールは次を参照

http://ocmock.org

何ができるのか(書き方)は次を参照

http://ocmock.org/features/

stubの戻り値でプリミティブ値を使う場合

andReturnValueを使い、OCMOCK_VALUEでラップする

id blog = [OCMockObject mockForClass:[Blog class]];
[[[blog stub] andReturnValue:OCMOCK_VALUE((BOOL){NO})] isPublished];

もしくは

id blog = [OCMockObject mockForClass:[Blog class]];
BOOL flag = NO;
[[[blog stub] andReturnValue:OCMOCK_VALUE(flag)] isPublished];

stubの引数が何でも良い場合

[OCMArg any]

動かすオブジェクトと同じオブジェクトのメソッドをテストする場合

Blogクラスにfindとeditを用意し、edit実行時にfindが呼ばれているかどうかをチェック

※モックオブジェクトのverifyを呼ぶのを忘れないように

プロダクトコード

#import "Blog.h"

@implementation Blog

-(void)find:(int)no
{
    
}

-(void)edit:(int)no
{
    [self find:no];
}

@end

テストコード

-(void)testEdit
{
    id blog = [OCMockObject partialMockForObject:[[Blog alloc] init]];
    [[blog expect] find:1]; // find:が引数1で呼ばれたかどうか?
    
    [blog edit:1];  // テストしたいメソッドを実行
    
    [blog verify];  // verifyで結果が検証される
}

スタブで引数にポインタを渡す場合

設定したいスタブコード

    [[[outputStream stub] andReturnValue:OCMOCK_VALUE((int){[sendData length]})] write:[OCMArg anyPointer] maxLength:[sendData length]];

実行するとwrite:[OCMArg anyPointer]の部分で次のエラーが出ます。

"Argument type '*' not supported."

そこで対象となるメソッドのSignatureを変更

    @interface NSOutputStream (OCMockFix)
    + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)selector;
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector;
    @end
    
    @implementation NSOutputStream (OCMockFix)
    + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)selector {
        if (sel_isEqual(selector, @selector(write:maxLength:))) {
            // Original signature is "i@:*I". Change '*' to '^C'
            return [NSMethodSignature signatureWithObjCTypes:"i@:^CI"];
        } else {
            return [super instanceMethodSignatureForSelector:selector];
        }
    }
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
        return [[self class] instanceMethodSignatureForSelector:selector];
    }
    @end

これでスタブが設定できました。

非同期処理のテスト

subMessageが2回連続シリアル非同期で呼ばれることを確認したい。

1.subMessageが呼ばれるとフラグと回数を設定するコードを実行させる。(ブロックで渡す)

2.2回呼ばれるので2回expectを設定しておく。

3.フラグが設定されるまで先に進まないようにループを組んでおく。  (subMessageが呼ばれない場合に備え、念のため1秒でタイムアウトさせる)

テストしたいコード

- (void)testMessage
{
    dispatch_queue_t testSerialDipatchQueue =
    dispatch_queue_create("gcd.TestSerialDispatchQueue",DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(testSerialDipatchQueue, ^{
        [self subMessage];
        [self subMessage];
    });
    
    dispatch_release(testSerialDipatchQueue);
}

テストコード

id target = [OCMockObject mockForClass:[Target class]];

__block BOOL isSuccessed = NO;
__block int count = 0;

void (^theBlock)(NSInvocation *) = ^(NSInvocation *invocation) {
    // subMessageが呼ばれたら設定
    isSuccessed = YES;
    count++;
};

[[[target expect] andDo:theBlock] subMessage];
[[[target expect] andDo:theBlock] subMessage];

[target testMessage];

NSDate *timeoutTime =  [NSDate dateWithTimeIntervalSinceNow:1];
while (isSuccessed == NO) {
    NSDate *currentTime = [NSDate date];
    if ([currentTime compare:timeoutTime] == NSOrderedDescending) {
        // 無限ループ回避(正常時は1秒以内にループを抜ける想定)
        break;
    }
}

STAssertTrue(count == 2, @"");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment