2009-07-06 6 views
6

これをテストするにはどうすればいいのでしょうか。私はパラメータを取るメソッドを持っており、そのパラメータのいくつかのプロパティに基づいて別のオブジェクトを作成し、それを操作します。私が確認したいことObjective-Cのメソッド内部オブジェクトを単体テストする方法はありますか?

- (void) navigate:(NavContext *)context { 
    Destination * dest = [[Destination alloc] initWithContext:context]; 
    if (context.isValid) { 
    [dest doSomething]; 
    } else { 
    // something else 
    } 
    [dest release]; 
} 

がcontext.isValidがtrueの場合、そのdoSomethingのはDESTで呼び出されていることですが、私はそれをテストする方法がわからない(あるいはそれが偶数の場合:コードは次のようになりますOCMockやその他の伝統的なテストメソッドを使用して、そのオブジェクトがメソッドの範囲内で完全に作成されるため)。私はこれについて間違った方法をしていますか?

答えて

5

あなたは可能性がありOCMockを使用しますが、コードを変更してDestinationオブジェクトを取得するか、最初にモックオブジェクトで置き換えることができるシングルトンオブジェクトを使用する必要があります。

これを行うためのクリーンな方法は、おそらく

-(void) navigate:(NavContext *)context destination:(Destination *)dest; 

メソッドを実装することです。次へ-(void) navigate:(NavContext *)contextの実装を変更します。

- (void) navigate:(NavContext *)context { 
    Destination * dest = [[Destination alloc] initWithContext:context]; 
    [self navigate:context destination:dest]; 
    [dest release]; 
} 

これは、あなたのテストは、直接、余分なパラメータを持つメソッドを呼び出すことができます。 (他の言語では、単にdestinationパラメータのデフォルト値を指定することでこれを実装しますが、Objective-Cはデフォルトパラメータをサポートしていません)。

+0

これは最も簡単な答えですが、簡単なテストをサポートするために、他の場所で実際には必要ではありません。私はどこかで妥協をする必要があると思います:) – Kevlar

+0

別のDestinationオブジェクトを代用できるようにするには、何らかの方法でそれを渡す必要があります。これが私が知る最もクリーンな方法です。 –

0

method swizzlingなどの興味深いテクニックを使用して完全に可能ですが、間違った方法が考えられます。単体テストからdoSomethingを呼び出す効果を観察する方法が全くない場合は、doSomething実装の詳細を呼び出すという事実ではありませんか?

(あなたがこのテストを行うとしたら、あなたの目的を達成する一つの方法は、あなたのユニットテストを通知し、その後doSomethingへの呼び出しに渡すものとDestinationdoSomething方法を置き換えることになる。)

+0

私はメソッドが呼び出されたことをテストしないと大したことではないと思います。私はまだTDD /ユニットテストの新機能ですので、まだベストプラクティスのすべてを知りません:) – Kevlar

1

私が確認したいのは、context.isValidがtrueの場合です。それは何かがdestで呼び出されます

私はここで間違ったことをテストしているかもしれないと思います。あなたは、(私が望む)ブールステートメントがObjCで正しく動作することを前提としています。代わりにContextオブジェクトをテストしたくないですか? context.isValidの場合、[dest doSomething]ブランチが実行されることが保証されます。

+0

私はコンテキスト・オブジェクトを嘲笑しています。このテストでは、どのように作成されても気にしないので、メソッドがコンテキストのプロパティに基づいて適切な処理を行うことに気をつけます。 – Kevlar

+0

さらに、navigate:の実装が変更された場合でも、doSomethingが呼び出されたことを確認したい場合があります。 –

+0

ああ、十分に、私は少し短い目撃されていた。 私はBJホーマーのアプローチが好きです。 – EightyEight

0

私は、この状況ではファクトリメソッドを使いたいと思います。

@interface Destination(Factory) 
+ (Destination *)destinationWithContext:(NavContext *)context; 
@end 

@implementation Destination(Factory) 
+ (Destination *)destinationWithContext:(NavContext *)context 
{ 
    return [[Destination alloc] initWithContext:context]; 
} 
@end 

私はその後FakeClassを行います

#import "Destination+Factory.h" 

@interface FakeDestination : Destination 
+ (id)sharedInstance; 
+ (void)setSharedInstance:(id)sharedInstance; 
// Note! Instance method! 
- (Destination *)destinationWithContext:(NavContext *)context; 
@end 

@implementation FakeDestination 
+ (id)sharedInstance 
{ 
    static id _sharedInstance = nil; 
    if (!_sharedInstance) 
    { 
     _sharedInstance = [[FakeDestination alloc] init]; 
    } 
    return _sharedInstance; 
} 
+ (void)setSharedInstance:(id)sharedInstance 
{ 
    _sharedInstance = sharedInstance; 
} 
// Overrides 
+ (Destination *)destinationWithContext:(NavContext *)context { [FakeDestination.sharedInstance destinationWithContext:context]; } 
// Instance 
- (Destination *)destinationWithContext:(NavContext *)context { return nil; } 
@end 

これを設定すると、あなただけの+ (Destination *)destinationWithContext:(NavContext *)context;

ためswizzle the class methodsに必要な今、あなたがに設定されている:

id destinationMock = [OCMock mockForClass:FakeDestination.class]; 
// do the swizzle 
[FakeDestination setSharedInstance:destinationMock]; 
[[destinationMock expect] doSomething]; 
// Call your method 
[destinationMock verify]; 

これはかなりの量のコーディングですが、非常に再利用可能です。

関連する問題