web-dev-qa-db-ja.com

Webサービスの呼び出しを必要とするクラスを単体テストするにはどうすればよいですか?

Hadoop Webサービスを呼び出すクラスをテストしようとしています。コードはほとんど次の形式です。

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

例えばディレクトリ作成メソッド、フォルダー作成メソッドなどがあります。

コードが制御できない外部Webサービスを処理している場合、これを単体テストするにはどうすればよいですか? Webサービスのクライアント/応答を試してみて模倣することもできますが、これは最近よく目にするガイドラインである「所有していないオブジェクトを模倣しないでください」を破ります。私はダミーのWebサービス実装をセットアップできました。それでも「単体テスト」を構成するのでしょうか、それとも統合テストでしょうか?この低いレベルで単体テストを行うことは不可能ですか?TDDの実践者はこれをどのように行うのでしょうか?

22
Chris Cooper

私の意見では、統合テストではなく単体テストの場合は、Webサービスの呼び出しを模擬する必要があります。

単体テストでは、外部Webサービスが機能しているかどうか、または外部Webサービスとの統合が正しいかどうかをテストしないでください。 TDDについて独断的にならずに、単体テストを統合テストに変えることの副作用は、実行が遅くなる可能性が高く、fast単体テストが必要になることです。

また、Webサービスが一時的にダウンしているか、正しく動作していない場合、これによりユニットテストが失敗しますか?それは正しくないようです。ユニットテストが失敗する理由は1つだけです。その「ユニット」のコードにバグがある場合です。

ここで関連するコードの唯一の部分は...do something with response...。残りをモックする。

42
Andres F.

単体テストをしているときは、「所有していないオブジェクトをモックしないでください」には同意しません。

存在のモックの目的は、私たちが所有しないモジュール、ライブラリ、クラスがあるという事実です。

あなたのシナリオに対する私の提案は、Webサービス呼び出しを模擬することです。

モジュールにデータを返すようにモックをセットアップします。
たとえば、返されたデータがnullの場合、返されたデータが有効な場合など、すべてのシナリオをカバーするようにしてください。

また、所有するコードの開発者としての責任は、作成しているコードがすべてのシナリオで期待どおりに実行されることを保証することです。

6
Venu b

このテストにはEasyMockのようなものを使用します。フレームワークのモックは、クラスの外部依存関係を削除する理想的な方法であり、テスト中に外部依存関係の結果を完全に制御できます。あなたの例を少し拡張するには:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

最初に行う必要があるのは、Jerseyを使用してWebResourceを取得し、Webサービスを別のクラスに呼び出すクラスのロジックを抽出することです。このクラスのインターフェイスを作成すると、動作を指示できるモックを作成できます。

このインターフェイスが作成されたら、EasyMockを使用してモックを作成できます。モックは、テストケースに従って指定されたオブジェクトを返します。上記の例は、基本的な模擬テストを構築する方法とインターフェースがどのように機能するかを単純化したものです。

フレームワークのモックの詳細については、 この質問 を参照してください。また、この例ではJavaの使用を前提としていますが、モックフレームワークはすべての言語で使用でき、実装方法は異なりますが、一般的に同じように機能します。

1
Richard

この場合、モックは受け入れられますが、必要ありません。単体テストmethod()の代わりに、応答を処理する部分だけを単体テストします。

ResponseData(適切な種類の)を取る関数を抽出して、アクションを実行します。

モックする代わりに、ResponseDataオブジェクトを作成して渡すだけです。

サービスのcallingを完全な統合テストに任せることができます-これはmethod()全体をカバーします

1
Daenyth

私がやったこと、そしてそれはうまくいきます:

  1. すべてのコードでプロキシ経由でウェブサービスを呼び出します。
  2. プロキシは、プロキシを使用しているかどうかを静的に認識するクラスを呼び出し、それに応じてリダイレクトします。モックは、リクエストごとに特定の応答を返すハッシュマップです。
  3. 次の順序でテストを数回実行します。

3.1最初に、すべてのWebサービスがテストされます。各マシンから、開発者のマシンでさえ。これらは実際のWebサービスですが、開発環境で実行されています。これは、Webサービスがダウンしたり、誤った値に応答したりすることは決してないことを意味します。

3.2次に、アプリケーション内部のすべての単体テストが実行されます。これは、すべてのWebサービスがモックされ、3.1と同じテストを実行してテストされ(それらも通過する必要があります。そうでない場合、モックは間違っています)、実際に使用されているかのように実際のアプリケーションによって呼び出されます。モックが間違っている場合は、3.1でテストを実行し、それらの値(要求、応答)をHashMapに記録できます。

3.3次に3.2と同じテストが実行されますが、今回は開発環境で実行されている実際のWebサービスに対して実行されます。

これらがすべて完了したら、実際の本番環境では、各Webサービスに実際のアドレスを指定するだけで済みます。うまくいけば、これは設定をあまり変更する必要がありません。

0