2013-04-12 2 views
34

最近私はいくつかのjUnitとMockitoの質問に尋ねましたが、私はまだそれをやり遂げるのに本当に苦労しています。チュートリアルはすべて非常に簡単な例ですので、私のクラスで動作するようにテストケースをスケールアップするのには苦労しています。Mockitoを使ってスタブを実行し、テストのためのメソッドを実行する

私は現在、Webアプリケーションのエージェントの1つにあるメソッドのテストケースをいくつか記述しようとしています。このメソッドは、エージェント内のいくつかのオブジェクトを検証するために、他のいくつかのメソッドと対話します。私はちょうど今この1つの方法をテストしたいと思う。

は、ここで私が何をしようとしているものです:

  1. がそうのように私のエージェントのMockitoオブジェクトを作成します。Mockitoを使用して

    MyProcessingAgent mockMyAgent = Mockito.mock(MyProcessingAgent.class);

  2. セットアップスタブ(うまくいけば右項)。そんなとき:

  3. ので、私のような方法を実行してみてください。

    List myReturnValue = mockMyAgent.methodThatNeedsTestCase();

私はmyReturnValueで物事に期待し、私は、デバッグしようとしたので、代わりに0を受信しました。メソッドを呼び出すと、決して実行されません。メソッドの最初の行には触れないデバッグポイントがあります。

クラスの1つのメソッドでコードを実行したいが、偽装された値を返すために、クラス内の他のメソッド(外部のデータベースとやりとりする)が必要な場合。これはMockitoで可能ですか?

私の現在のアプローチ方法は正しいテストスタイルではありませんが、私はどのように前進するかはわかりません。私のクラスを模擬し、普通のように1つのメソッドを実行させることができますか?このメソッドのテスト中にデータアクセスを処理する必要がないように、他のメソッドをスタブして指定した値を返すことはできますか?

答えて

48

MockSpyを混同しています。

モックですべてのメソッドがスタブされ、「スマートリターンタイプ」を返します。これは、動作を指定しない限り、模擬クラスのメソッドを呼び出すとは何もしません。です。

スパイでは、クラスの元の機能はまだありますが、スパイでメソッドの呼び出しを検証し、メソッドの動作をオーバーライドすることもできます。

static class TestClass { 

    public String getThing() { 
     return "Thing"; 
    } 

    public String getOtherThing() { 
     return getThing(); 
    } 
} 

public static void main(String[] args) { 
    final TestClass testClass = Mockito.spy(new TestClass()); 
    Mockito.when(testClass.getThing()).thenReturn("Some Other thing"); 
    System.out.println(testClass.getOtherThing()); 
} 

出力は次のとおりです:

何が欲しいのは

MyProcessingAgent mockMyAgent = Mockito.spy(MyProcessingAgent.class); 

簡単な例である

Some Other thing 

NB:あなたは本当にクラスであることのための依存関係を模擬するようにしてくださいではないクラスそのもの。

+0

スパイについての私の声明を見て、あなたが同意しない場合は教えてください。 –

+0

@JohnBあなたは正しいと思います。私は答えによって拡大しました。 OPがメソッドを内部的に呼び出すことはわかりませんでした。 –

+0

詳細な説明をお寄せいただきありがとうございます。だから、両方の答えが(明確にするために)推薦しているのは、私がMockで間違ったクラスを攻撃しているということです。代わりに私はクラスに必要なすべてのデータアクセスオブジェクトを嘲笑してから、テストのためにクラスの通常のインスタンスを使用する必要があります。 – Kyle

5

したがって、テスト中のクラスを嘲笑する考えは、テストの実践には否定できない。あなたはこれをしてはいけません。あなたがそうしたので、あなたのテストはあなたのクラスではなく、Mockitoの模擬クラスに入っています。

スパイは、スパイされたクラスの周りにラッパー/プロキシーを提供するだけなので、機能しません。実行がクラス内に入ると、プロキシを経由せず、スパイには当たらない。更新:私はこれをSpringのプロキシに当てはまると信じていますが、Mockitoのスパイには当てはまりません。私は方法m1()m2()を呼び出すシナリオを設定しました。私はオブジェクトをスパイし、m2()doNothingにスタブします。テストでm1()を呼び出すと、クラスのm2()に達しません。 Mockitoはスタブを呼び出します。だから頼まれていることを達成するためにスパイを使うことは可能です。しかし、私はそれを悪い習慣(IMHO)とみなすことを繰り返し述べる。

テスト対象のクラスが依存するすべてのクラスをモックする必要があります。これにより、テスト対象のメソッドによって呼び出されるメソッドの動作を、それらのメソッドが呼び出すクラスを制御するという点で制御できます。

クラスが他のクラスのインスタンスを作成する場合は、ファクトリの使用を検討してください。

+0

+1は、嫌悪感のような言葉の使用のために、私はあなたのポイントを得ると思います。私はまだテストの周りに私の頭をラップしようとしている、私はちょうど完全に間違った角度から来たと思います。 – Kyle

+1

はおそらくA単語の真です。あなたがユニットテストを行っているという事実は、良い兆候です。がんばり続ける。乾杯! –

+0

'+ 1'良い答え – andyb

3

あなたはほぼ手に入れました。問題は、ユニットテスト用にClass Under TestCUT)が構築されていないことです。は実際にはでした。TDD 'でした。

このように…

  • 私はクラスの機能をテストする必要がある - のは
  • MyFunctionをそれを呼びましょうこの関数は、関数別のクラス/サービス/データベース上の関数の呼び出し
  • は上の別のメソッド呼び出しを行いますユニットテストでCUT

  • がその上に具体的なCUTまたは@Spyを作成する必要があります
  • ことはでき@Mock他のクラス/サービス/データベースのすべて(すなわち、外部依存関係)
  • あなたでしスタブCUTに呼び出される他の機能がありますが、厳密でないコードの実行を避けるために、それは本当にユニットテストは

をどのように行われる必要がありますされていませんテストすれば、そのコードを抽象化して、@Mockにできるものにすることができます。この非常に簡単な例では

、オブジェクトを作成する機能が

public void doSomethingCool(String foo) { 
    MyObject obj = new MyObject(foo); 

    // can't do much with obj in a unit test unless it is returned 
} 

をテストするのは難しいだろう。しかし、我々が抽象化されているとしてMyObjectにを取得するためにサービスを利用する機能は、テストするのは簡単ですこのメソッドをテスト可能にする何かにコードをテストするのは難しい/不可能です。

public void doSomethingCool(String foo) { 
    MyObject obj = MyObjectService.getMeAnObject(foo); 
} 

MyObjectServiceは嘲笑とも.getMeAnObject()はFOO変数と呼ばれることを検証することができるように。あなたのケースで行う方法

+0

の場合_myFunction_と_myFunction2_が** CUT **の一部で、_myFunction_が_myFunction2_を呼び出している場合、Spyベースの部分的な嘲笑は唯一のオプションではないでしょうか? _myFunction2_は** CUT **の外に移動し、** CUT **の不可欠な部分であるにもかかわらず "注入"されるべきですか?私は何が欠けていますか? – Nishith

+0

私は何かが足りないとは思わない。スパイベースの部分模擬が最良の選択肢ですが、両方の方法を一緒にテストすることは可能です。私の例は、* CUT *のメソッドが、ビヘイビアを検証したり、値をアサートしたりするオブジェクトを作成し、ファクトリまたは外部サービスを使用して* CUT *をリファクタリングしてそれらを作成することができるモックされて注入されることができるオブジェクト。 – andyb

+0

ありがとう...意味がある... – Nishith

0

SHORT ANSWER

int argument = 5; // example with int but could be another type 
Mockito.when(mockMyAgent.otherMethod(Mockito.anyInt()).thenReturn(requiredReturnArg(argument)); 

LONG ANSWER

は実際にあなたが何をしたいのかたぶん、あなたはしなかった、少なくともJavaの8で、可能です私はそれを可能にするJava 8を使用しているので、他の人がこの回答を得ています。この質問はJava 8のリリース前です(他の関数に値を渡すことはできません)。

DataBaseクエリの呼び出しをシミュレートしましょう。このクエリは、FreeRoms = XとStarNumber = Yを持つHotelTableのすべての行を返します。 テスト中に期待することは、このクエリが別のホテルのリストを返すことです。返されるホテルはすべて同じ値XとYを持ちます。他の値と私は私のニーズに応じてそれらを決定します。次の例は単純ですが、もちろん複雑にすることもできます。

は、だから私は、異なる結果をお返しする関数を作成しますが、それらのすべてがFreeRomsの= XとStarNumber = Yを持つ

static List<Hotel> simulateQueryOnHotels(int availableRoomNumber, int starNumber) { 
    ArrayList<Hotel> HotelArrayList = new ArrayList<>(); 
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Rome, 1, 1)); 
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Krakow, 7, 15)); 
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Madrid, 1, 1)); 
    HotelArrayList.add(new Hotel(availableRoomNumber, starNumber, Athens, 4, 1)); 

    return HotelArrayList; 
} 

たぶんスパイが(試してみてください)良いですが、私は上でこれをしませんでした嘲笑されたクラス。ここで私はどのように(anyInt()の値に注意してください):

//somewhere at the beginning of your file with tests... 
@Mock 
private DatabaseManager mockedDatabaseManager; 

//in the same file, somewhere in a test... 
int availableRoomNumber = 3; 
int starNumber = 4; 
// in this way, the mocked queryOnHotels will return a different result according to the passed parameters 
when(mockedDatabaseManager.queryOnHotels(anyInt(), anyInt())).thenReturn(simulateQueryOnHotels(availableRoomNumber, starNumber)); 
関連する問題