QA@IT

ocmockを使ったxctestに関して

3703 PV

お世話になります

ocmockを使ってxctestを行っているのですが、初歩的なところなのかよくわからないですが、詰まってしまいました
モックオブジェクトを用意して、メソッドが呼ばれてるか確認してるのですが、ログ自体を見る限りは呼ばれているのに、
何故か、テストが失敗してしまっています。

テストコードが(一部抜粋)

#import <XCTest/XCTest.h>
#import "OCMock.h"
#import "ViewController.h"
#import "DrawingView.h"

@interface ViewControllerTests : XCTestCase

@end

@implementation ViewControllerTests{
    ViewController *vc;
    id mockelaser;
    id mockchook1;
    id mockchook2;
    id mockchook3;
    id mockchook4;
    id mockchook5;
    id mockmenu;
    id mockcurdraiwingview;
    id mockpastdraiwingview;
    id mockViewCotnroller;
}

- (void)setUp
{
    [super setUp];
//    
    //UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main_iPhone" bundle:nil];
    //vc = [storyboard instantiateViewControllerWithIdentifier:@"kokuban_iphone"];
    vc = [[ViewController alloc] init];

    //とりあえず、モックを作りまくる
    mockelaser = [OCMockObject mockForClass:[UIButton class]];
    mockchook1 = [OCMockObject mockForClass:[UIButton class]];
    mockchook2 = [OCMockObject mockForClass:[UIButton class]];
    mockchook3 = [OCMockObject mockForClass:[UIButton class]];
    mockchook4 = [OCMockObject mockForClass:[UIButton class]];
    mockchook5 = [OCMockObject mockForClass:[UIButton class]];
    mockmenu    = [OCMockObject mockForClass:[UIButton class]];
    mockcurdraiwingview    = [OCMockObject mockForClass:[DrawingView class]];
    mockpastdraiwingview    = [OCMockObject mockForClass:[DrawingView class]];
    mockViewCotnroller = [OCMockObject mockForClass:[ViewController class]];


    vc.chook1 = mockchook1;
    vc.chook2 = mockchook2;
    vc.chook3 = mockchook3;
    vc.chook4 = mockchook4;
    vc.chook5 = mockchook5;
    vc.menu = mockmenu;
    vc.curDrawingView = mockcurdraiwingview;
    vc.pastDrawingView = mockpastdraiwingview;
    //[self.vc performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES];
    // Put setup code here. This method is called before the invocation of each test method in the class.
}


元のコードが

#import "ViewController.h"
#import "Common.h"
#import "Line.h"

#define INDICATOR_TAG 100
#define GAD_SIMULATOR_ID @"Simulator"

@interface ViewController ()

-(void)startAnimating;
-(void)stopAnimating;

-(IBAction) showActionSheet :(id) sender ;
-(IBAction)showEraseSheet:(id)sender;
-(void)store;
-(void)doUndo:(id)sender;
-(void)doAllUndo:(id)sender;
-(void)onTouchUpInsideBtnCapture;

-(void)gotoNext;
-(void)showBuyConfirmAlert;
-(void)gotoAppStore;

@end

@implementation ViewController
@synthesize  kokubanMode;
@synthesize  yukiMode;
@synthesize  billingMode;
@synthesize  linedepth;
@synthesize  penWhite,penRed,penYellow,penBlue,penBlack,penGreen,penAlpha;

- (void)startAnimating {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

// 何か処理を行って、準備できたらこれを呼ぶ
- (void)stopAnimating {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

-(void)setPenColor:(NSInteger)idx {
    CGFloat components[] = {
        //   r     g     b     a
        1.0f, 1.0f, 1.0f, 1.0f, // 0:白
        246 / 255.0f, 171 / 255.0f, 171 / 255.0f,  1.0f,    // 0:ピンク
        171 / 255.0f, 177 / 255.0f, 244 / 255.0f, 1.0f, // 1:青
        238 / 255.0f, 241 / 255.0f, 174 / 255.0f, 1.0f, // 2:き色
        171 / 255.0f, 244 / 255.0f, 177 / 255.0f, 1.0f, // 3:緑
        0.0f, 0.0f, 0.0f, 0.0f, // 0:消す
    };
    if ((idx >= 0) && (idx <= 5)) {
        penRed      = components[idx*4+0];
        penGreen    = components[idx*4+1];
        penBlue     = components[idx*4+2];
        penAlpha    = components[idx*4+3];
    }
    NSLog(@"ooooooooooooooooooooooooooo");
}



#pragma mark -
#pragma mark 白チョークに変更
-(void) chook1 :(id) sender {
    [self Allstand:1];
    [self setPenColor:0];
    NSLog(@"testeststeststtsetetsestttttststwesssetsts");
    kokubanMode = YES;
}
#pragma mark -
#pragma mark チョークを元の位置に戻す
-(void) Allstand : (int) stand {
    switch (select_chook) {
        case 1:
            chook1.transform    = CGAffineTransformMakeRotation(0);
            break;
        case 2:
            chook2.transform    = CGAffineTransformMakeRotation(0);
            break;
        case 3:
            chook3.transform    = CGAffineTransformMakeRotation(0);
            break;
        case 4:
            chook4.transform    = CGAffineTransformMakeRotation(0);
            break;
        case 5:
            chook5.transform    = CGAffineTransformMakeRotation(0);
            break;
        case 6:
            crower_on.alpha     = 0;
            crower.enabled      = YES;
            break;
        default:
            break;
    }
    switch (stand) {
        case 1:
            chook1.transform    = CGAffineTransformMakeRotation(10);
            break;
        case 2:
            chook2.transform    = CGAffineTransformMakeRotation(10);
            break;
        case 3:
            chook3.transform    = CGAffineTransformMakeRotation(10);
            break;
        case 4:
            chook4.transform    = CGAffineTransformMakeRotation(10);
            break;
        case 5:
            chook5.transform    = CGAffineTransformMakeRotation(10);
            break;
        case 6:
            crower_on.alpha     = 100;
            crower.enabled      = NO;
            break;
        default:
            break;
    }

    select_chook = stand;

}

viewcontrollerのモック作ってテストしてるのがダメなんですかね。。。
ログを見る限り、NSLogで出力してるものは出ているので、よくわからず。。。

ご教授お願いします
よろしくお願いいたします

  • setupしかない様に見えますが、testメソッドは作ってないのですか?
    「テストが失敗した」とは何で判断されたのですか?
    -
  • mockの作り方変更とexpectの追加のどちら(またはそれ以外の修正?)でうまく行ったか書いてもらっていいですか?
    他の人の参考になるので。
    -
  • mockの作り方変更です。
    頂いた回答のコードでうまく行きました。
    かなり理解が深まりました。
    ありがとうございます!
    -

回答

使ったことがないのですが、テストとしては、メソッド二つ(AllstandとsetPenColor)が指定した引数(1と0)で呼ばれたかを期待値としているという事であってますか?

そうするとmockViewCotnrollerが呼ばれた事を検知しないといけないわけですが、
vcmockViewCotnrollerに何も関連性がありませんよね。
ですので vcの chook1を呼び出していますが、これはmockViewCotnrollerの知らないところで起こっていることであり、そこで起きている Allstand, setPenColorの呼び出しも検知できていないのだと思います。
(NSLogが出ているというのもvcの chook1が呼ばれたという事しか示しておらず、それによって mockViewControllerが何かを感知できたかどうかはわからないかと。)

調べてみましたがインスタンスからもmockが作れるそうなので、mockViewControllerはインスタンスを元に作成するといいのではないかと思います。
テストメソッドを以下の様にすればいけるんじゃないでしょうか(setup直すと長くなるので、ViewControllerのモックもテストメソッドの中で作ってしまいました)。

- (void)testChook1
{
    // mockViewControllerの代わりをvcから作成する
    id mockVC = [OCMockObject partialMockForObject:vc];
    [[mockVC expect] Allstand:1];
    [[mockVC expect] setPenColor:0];
    [vc chook1:vc.chook1];
    [mockVC verify];
}

試してないので違ってたらごめんなさい。


追記

エラーが unexpected method invoked: chook1 (予期せぬ chook1の呼び出し) なので、chook1の呼び出しもexpectしなければいけなかったのかもしれません。
(提示してくれた元の mockViewControllerのコードに [[mockViewController expect] chook1]; を追加すればいいだけだったのかも)。

編集 履歴 (2)
  • うまくいきました!ありがとうございます -

エラーはこういった感じです

Test Case '-[ViewControllerTests testChook1]' started.
<unknown>:0: error: -[ViewControllerTests testChook1] : OCMockObject[ViewController]: unexpected method invoked: chook1 
    expected:   Allstand:1
    expected:   setPenColor:0
編集 履歴 (0)
  • あ、そういうエラーですか。ちょっと回答に追記します。 -

失礼しました。
テストコードがこちらです

- (void)testChook1
{
    [[mockViewCotnroller expect] Allstand:1];
    [[mockViewCotnroller expect] setPenColor:0];
    [vc chook1:vc.chook1];
    [mockViewCotnroller verify];
}

chook1メソッドが呼ばれた際に期待するメソッドを定義して、verifyしたつもりです。

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。