2011-12-27 6 views
5

私は、Mockito、EasyMock、JMock、PowerMockなどのいくつかのJava Mocking APIを読んでいます(そして実験しています)。私はさまざまな理由からそれぞれが好きですが、最終的にはMockitoを決めました。 注記でも、これはではないどのフレームワークを使用するかについての質問 - 実際には模擬フレームワークに質問がありますが、APIは(明らかに)異なっているように見えます。動作確認の価値

多くのことと同じように、チュートリアルを読んだり、サンプルに従ったり、サンドボックスプロジェクトでいくつかのコードサンプルを試したりします。しかし、それを実際に使用する時間が来たら、あなたは泣き始める - それが私の居場所です。

I 実際にはのように、実際には嘲笑のアイデアが好きです。そして、はい、私は、テスト中のクラスにあまりにも強く結びついている "脆い"テストにつながる嘲笑に関する苦情を認識しています。しかし、自分自身でそのような実現に来るまで、私は本当に私の単体テストにいくらかの良い価値を加えることができるかどうかを見る機会を与えたいと思っています。

私は現在、単体テストでモックを積極的に使用しようとしています。 Mockitoは、スタブとモックの両方を可能にします。 getMaxSpeed()メソッドを持つCarオブジェクトがあるとします。 Mockitoでは、我々はそうのようにそれをスタブできます

Car mockCar = mock(Car.class); 
when(mockCar.getMaxSpeed()).thenReturn(100.0); 

この「スタブ」Carオブジェクトを常に私たちの車の最高速度として100.0を返すように。

私の問題は、単体テストのテストを書いた後、すでに...私がやっているのは、私の協力者をスタブしていることです!私は単一のモックメソッド(verifyなど)を私に利用することはできません!

私は「スタブの状態が」になっていることに気がつきました。このすべての読書、そしてこのすべての興奮は私の単体テストでモックを使用するようになっています。私は行動検証のための単一のユースケースを考えることはできません。

Fowlerのarticleと他のBDD形式の文献をバックアップして再読み込みしましたが、それでもテストダブルコラボレーターの動作検証の価値は「得られません」。

私はを知っています私は何かが不足していることを知っています。例えば、このCarクラスを使用して、具体的な例(または一連の例もあります)を教えてもらえますか?また、動作検証ユニットテストが状態検証テストに適していることを実証しますか?

正しい方向に任意のナッジをお寄せいただきありがとうございます。

+0

あなたのクラスの1つを教えてください。たとえば、上記でスタブされているCarのクライアントである。おそらく、モックを使ってテストする方法を提案することができます。 –

+0

こんにちはTom - 私はJB Nizetの例がCarよりも好きです。その例は、JBの説明とともに、本当に私の混乱の原因を強調しています。 JBの回答の下に私のコメントはこれを説明しています。 – IAmYourFaja

答えて

2

まあ、テスト対象のオブジェクトが計算値を持つ協力者を呼び出し、そのテストが計算が正しいことをテストすると想定されている場合は、模擬コラボレータを検証するのが適切です。例:

private ResultDisplayer resultDisplayer; 

public void add(int a, int b) { 
    int sum = a + b; // trivial example, but the computation might be more complex 
    displayer.display(sum); 
} 

明らかに、この場合には、あなたが表示機能を模擬しなければならない、と2と3はaddメソッドの引数である場合にその表示方法は、値5と、呼び出されていることを確認します。

あなたの協力者は、引数のない呼び出しゲッター、またはテストされたメソッドの直接入力である引数であれば、コードが2人の共同編集者から値を取得できない限り、スタブしても十分でしょう。適切な協力者が呼び出されたことを確認します。

例:

private Computer noTaxComputer; 
private Computer taxComputer; 

public BigDecimal computePrice(Client c, ShoppingCart cart) { 
    if (client.isSubjectToTaxes()) { 
     return taxComputer.compute(cart); 
    } 
    else { 
     return noTaxComputer.compute(cart); 
    } 
} 
+0

ありがとうJB!私は今私の混乱の根源にあると思う。 2番目のパラグラフでは、「...ディスプレーヤーをモックして、その表示方法が呼び出されていることを確認する必要があります。値5 ...」私はディスプレーヤーが表示していることを確認する必要がある理由を理解しています5の値...私が理解していないのは、なぜ、「displayer.display」が呼び出されたかを検証する必要があるということです。なぜ、assertEquals(displayer.getDisplay()、5) 'というJUnitのアサートを書くことができないのですか?私は 'display(int)'メソッドが実行されたことをチェックして単体テストに値を追加する理由が分かりません。任意のアイデア/カウンターグア?どうも! – IAmYourFaja

+0

ディスプレーヤーの表示方法は、ゲッターでアクセス可能なフィールドに値を格納するよりもはるかに複雑な処理を行う可能性があるためです。ストリーム、LCDディスプレイ、データベースなどに送ることができます。フィールドに格納しても、実際のディスプレイライヤーの構築は、データベース、JMSエンジンまたは他の複雑なインフラストラクチャ。 –

+0

ディスプレーヤーはメソッド中に数回呼び出すこともできます:1回は2回、1回は3回、もう1回は5回です。各中間状態を確認したい場合は、ディスプレーヤーをモックする以外の方法はありません。 –

2

私は@JB Nizetの答えを好きですが、ここでは別の例です。何らかの変更を加えてHibernateを使ってCarをデータベースに保持したいとします。ですから、このようなクラスがあります:加速する方法をテストするには

public class CarController { 

    private HibernateTemplate hibernateTemplate; 

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { 
    this.hibernateTemplate = hibernateTemplate; 
    } 

    public void accelerate(Car car, double mph) { 
    car.setCurrentSpeed(car.getCurrentSpeed() + mph); 
    hibernateTemplate.update(car); 
    } 
} 

を、あなただけのスタブを使用することができますが、競合のテストを持っていないでしょう。

public class CarControllerTest { 
    @Mock 
    private HibernateTemplate mockHibernateTemplate; 
    @InjectMocks 
    private CarController controllerUT; 

    @Test 
    public void testAccelerate() { 
    Car car = new Car(); 
    car.setCurrentSpeed(10.0); 
    controllerUT.accelerate(car, 2.5); 
    assertThat(car.getCurrentSpeed(), is(12.5)); 
    } 
} 

このテストに合格して計算をチェックしていますが、車の新しい速度が保存されたかどうか、私たちは知りません。これを行うには、我々は、追加する必要があります。今すぐ

verify(hibernateTemplate).update(car); 

、あなたが過去最高速度を加速しようとした場合、あなたは加速して起こることではない更新を期待しているとします。その場合は、あなたが望む:

@Test 
public void testAcceleratePastMaxSpeed() { 
    Car car = new Car(); 
    car.setMaxSpeed(20.0); 
    car.setCurrentSpeed(10.0); 
    controllerUT.accelerate(car, 12.5); 
    assertThat(car.getCurrentSpeed(), is(10.0)); 
    verify(mockHibernateTemplate, never()).update(car); 
} 

このテストでは、CarControllerの現在の実装で通過していないだろうが、それはいけません。このケースをサポートするためにさらに多くの作業を行う必要があること、そしてこの場合はデータベースに書き込もうとしないことが要件の1つであることを示しています。

基本的には、何か起きたかどうかを確認するために、ベリファイを使用する必要があります。もしそれが起こったかしなかったという事実があなたがテストしようとしているものではないなら、それをスキップしてください。私が作った2番目の例を考えてみましょう。値が変更されていないため、更新が呼び出されたかどうかは関係ありません。その場合は、accelerateの実装がどちらの方法でも正しいので、2番目の例の検証手順をスキップできます。

私は多くの場合、使用を確認するケースを作っているようには聞こえません。それはあなたのテストを非常に脆弱にする可能性があります。しかし、起こるはずの重要なことが起こったことを「検証」することもできます。この上

0

私のテイクは、すべてのテストケースが

  • スタブ、プラス一つ以上のassert Sまたは
  • 一つ以上のverifyのいずれかを含まなければならないということです。

ただし、両方ではありません。

ほとんどのテストクラスでは、「スタブとアサート」テストケースとテストケースの「検証」が混在することになります。テストケースが「スタブとアサーション」を実行するか、「検証」を行うかは、共同作業者から返された値がテストにとって重要かどうかによって異なります。これを説明するには2つの例が必要です。

ドルの値を持つInvestmentクラスがあるとします。コンストラクタは初期値を設定します。これはaddGoldメソッドを持っています。これは、金の金額と金の金額の金額をオンスあたりの金額で増加させる方法です。金の価格を計算するPriceCalculatorと呼ばれる協力者がいます。私はこのようなテストを書くかもしれません。

public void addGoldIncreasesInvestmentValueByPriceTimesAmount(){ 
    PriceCalculator mockCalculator = mock(PriceCalculator.class); 
    when(mockCalculator.getGoldPrice()).thenReturn(new BigDecimal(400)); 
    Investment toTest = new Investment(new BigDecimal(10000)); 
    toTest.addGold(5); 
    assertEquals(new BigDecimal(12000), toTest.getValue()); 
} 

この場合、共同作業者メソッドの結果はテストにとって重要です。この時点でPriceCalculatorをテストしていないため、スタブします。メソッドが呼び出されていないと、投資価値の最終価値が正しくないため、検証する必要はありません。だから、私たちに必要なのはassertです。

Investmentから$ 100000を引き出した人がいる場合は、InvestmentクラスがIRSに通知するという要件があるとします。これを行うにはIrsNotifierという協力者を使用します。したがって、これについてのテストは、このように見えるかもしれません。この場合

public void largeWithdrawalNotifiesIRS(){ 
    IrsNotifier mockNotifier = mock(IrsNotifier.class); 
    Investment toTest = new Investment(new BigDecimal(200000)); 
    toTest.setIrsNotifier(mockNotifier); 
    toTest.withdraw(150000); 
    verify(mockNotifier).notifyIRS(); 
} 

、テストでは、共同研究者の方法notifyIRS()からの戻り値を気にしません。それとも無効かもしれない。重要なのは、メソッドが呼び出されたということだけです。このようなテストでは、verifyを使用します。このようなテストではスタブすることがあります(他の共同編集者を設定したり、別のメソッドから値を返すため)がありますが、検証するのと同じメソッドをスタブすることはまずありません。

同じコラボレーターメソッドでスタブと検証の両方を使用していることがわかっている場合は、おそらく自分自身に質問してください。本当に証明しようとしているテストは何ですか?戻り値はテストに関係しますか?これは通常、テストコードの匂いです。

これらの例が役に立ちます。

関連する問題