逆引きOCMock3

# 逆引きOCMock3

Objective-c / SwiftのMockライブラリの1つであるOCMockの使い方を用途別で主要なポイントのみに絞って纏める.
足りないものについては公式ドキュメントを参照して欲しい.

- [リファレンス](http://ocmock.org/reference/)
- [GitHub](https://github.com/erikdoe/ocmock)
- [CocoaPods](http://cocoadocs.org/docsets/OCMock)

## 基本
### クラスメソッドをmockする
```objc
// mock
id mock = OCMClassMock([SomeClass class]);
OCMStub([mock someMethod]).andReturn(0);

// 呼び出し
[[SomeClass class] someMethod];
```

### インスタンスメソッドをmockする
```objc
// mock
id mock = OCMPartialMock([[SomeClass alloc] init]);
OCMStub([mock someMethod]).andReturn(0);

// 呼び出し
// OCMPartialMockの返り値でもinitの際の返り値のどちらを使ってもOK
[mock someMethod];
```

### mockを終了する
```objc
[mock stopMocking];
```

### 制限
OCMockには以下の制限がある.

- (恐らく) あるクラスの全てのインスタンスについてmockすることはできない (インスタンス毎にmockが必要)
- [プリミティブ型の引数に関しては完全マッチしかできない](http://stackoverflow.com/questions/6325289/passing-primitives-to-an-ocmocks-stub)
- [Cの関数についてはmockすることはできない](http://stackoverflow.com/questions/21685695/how-to-mock-a-c-function-using-ocmock)

## 引数に制限を付ける

### Objective-cオブジェクト

```objc
// equal
OCMStub([mock someMethodWithArg:@"hoge"]).andReturn(0); // equal
// not equal
OCMStub([mock someMethodWithArg:[OCMArg isNotEqual:@"hoge"]).andReturn(0);
// all
OCMStub([mock someMethodWithArg:OCMOCK_ANY]).andReturn(0);
// より細かい判定
OCMStub([mock someMethodWithArg:[OCMArg checkWithBlock:^BOOL(id arg) {
    return YES; // 引数が正しければYESを返す
}]]).andReturn(0);
```

### プリミティブ型
完全マッチ以外を扱うことはできない.

```objc
// equal
OCMStub([mock someMethodWithInt:1]).andReturn(0);
```

## メソッドの挙動を変える

### 引数のみ差し替える

```objc
NSString* path = @"....";

id mock = OCMPartialMock(instance);
OCMStub([mock someMethodWithPath:OCMOCK_ANY]).andDo(^(NSInvocation* invocation){
    [invocation setArgument:&path atIndex:2];
}).andForwardToRealObject();
```

> Indices 0 and 1 indicate the hidden arguments self and _cmd, respectively

https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/#//apple_ref/occ/instm/NSInvocation/setArgument:atIndex:
なので, `atIndex`は2が第一引数になる.

### 他のメソッドに差し替える

```objc
id mock = OCMPartialMock(instance);

// クラスメソッドの場合はotherInstanceの箇所が[SomeClass class]になる
OCMStub([mock someMethodWithArg:OCMOCK_ANY]).andCall(otherInstance, @selector(otherMethodWithArg:));
```

### nilを返す

```objc
__block id returnValue = nil;

id mock = OCMPartialMock(instance);
OCMStub([mock someMethodWithArg:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
    [invocation setReturnValue:&returnValue];
});
```

## 呼びだされたことの確認
`OCMStub`の代わりに`OCMExpect`を使うことで, `OCMVerify`の呼び出し時に呼び出されたかのチェックを行うことができる.

```objc
id mock = OCMPartialMock(instance);
OCMExpected([mock someMethod]).andForwardToRealObject();

[mock someMethod]; // 呼び出し

OCMVerifyAll(mock);
```

### メソッド呼び出しを待つ

```objc
id mock = OCMPartialMock(instance);
OCMExpect([mock someMethod]).andForwardToRealObject();

[mock someMethod]; // 呼び出し

OCMVerifyAllWithDelay(mock, 5.f); // 第二引数は最大wait時間[s]
```

### 引数をチェックする
mockする時に引数に制限を設け, 呼びだされたかを確認することで実現できる.

```objc
id mock = OCMPartialMock(instance);
OCMExpect([mock someMethodWithArg:[OCMArg checkWithBlock:^BOOL(id arg) {
    return YES; // 引数が正しければYESを返す
}]]).andForwardToRealObject();

[mock someMethod:Arg]; // 呼び出し

OCMVerifyAll(mock);
```

### 同じメソッドについて複数回呼び出しを確認する
#### 一つ一つチェックしていくケース

`OCMExpect`は呼び出された回数が1以上であるかを見るだけなので, mockした内容は消えない.
追加で`OCMExpect`をすると, 最新から順に最初にマッチしたものが使用されるので, 新たに呼び出しの確認をすることができる.
`OCMObserverMock`でも同様のことが可能だが, 使い勝手が悪いのでおすすめしない. [^1]

```objc
id mock = OCMPartialMock(instance);

OCMExpect([mock someMethodWithArg:[OCMArg isEqual:A]]).andForwardToRealObject();
[mock someMethodWithArg:A];

OCMExpect([mock someMethodWithArg:[OCMArg isEqual:A]]).andForwardToRealObject(); // ここを消すと, 2回目の呼び出しはチェックされない
[mock someMethodWithArg:A];

OCMVerifyAll(mock);
```

#### 呼びだされた回数が知りたいケース
イケてる方法が恐らくないので, 自前でカウントアップする.

```objc
__block int counter = 0;
id mock = OCMPartialMock(instance);

// MRCの場合は__blockのみで良い.
__block __weak id weakMock = mock;
OCMStub([mock someMethod]).andDo(^(NSInvocation* invocation) {
    @synchronized(weakMock) {
        count++;
    } 
}).andForwardToRealObject();
```

`__block __weak`にしないと参照が消えずdeallocされなくなるので注意が必要. (テストなのであまり気にしないと思うが……)
排他制御の必要がない場合は, `@synchronized`ごと不要.


----
[^1]: OCMObserverMockの場合は指定した回数を超えるとクラッシュする挙動になってしまう.