1

Specs2とMockitoを使用してScalaコードをテストドライブしようとしています。私は3つすべてに比較的慣れておらず、モックされたメソッドがnullを返すのに苦労しています。

以下では(いくつかの名前の変更で転写されています)

  "My Component's process(File)" should  {

    "pass file to Parser" in new modules {
      val file = mock[File]
      myComponent.process(file)

      there was one(mockParser).parse(file)
    }

    "pass parse result to Translator" in new modules {
      val file = mock[File]
      val myType1 = mock[MyType1]

      mockParser.parse(file) returns (Some(myType1))
      myComponent.process(file)

      there was one(mockTranslator).translate(myType1)
    }

  }

「ファイルをパーサーに渡す」は、SUTにトランスレーター呼び出しを追加するまで機能しますが、mockParser.parseメソッドがnullを返したために終了します。これは、トランスレーターコードでは取得できません。

同様に、「解析結果を翻訳者に渡す」は、翻訳結果をSUTで使用しようとするまで渡されます。

これらの両方のメソッドの実際のコードがnullを返すことはありませんが、期待値が使用可能な結果を​​返すようにMockitoに指示する方法がわかりません。

もちろん、SUTにnullチェックを入れることでこれを回避することはできますが、nullを返さないようにし、代わりに、、およびを使用するので、むしろ避けたいとOption思いNoneますSome

良いScala/Specs2 / Mockitoチュートリアルへのポインターは、次のような行を変更する方法の簡単な例と同様に素晴らしいでしょう。

there was one(mockParser).parse(file)

nullを処理しない場合に、SUTでの継続的な実行を可能にするものを返すようにします。

これを理解しようと急いで、私はその行をに変更しようとしました

there was one(mockParser).parse(file) returns myResult

私が返したいタイプのmyResultの値を使用します。MatchResultそれは私のリターンタイプではなくそこを見つけることを期待しているので、それは私にコンパイルエラーを与えました。

重要な場合は、Scala2.9.0を使用しています。

4

3 に答える 3

4

まだご覧になっていない場合は、specs2ドキュメントの模擬期待ページをご覧ください。

コードでは、スタブは次のようになりますmockParser.parse(file) returns myResult

ドンの編集後に編集:

誤解がありました。2番目の例でそれを行う方法は良い方法であり、最初のテストでもまったく同じことを行う必要があります。

val file = mock[File]
val myType1 = mock[MyType1]

mockParser.parse(file) returns (Some(myType1))
myComponent.process(file)
there was one(mockParser).parse(file)

モックを使用した単体テストの考え方は常に同じです。モックがどのように機能するか(スタブ)、実行、検証する方法を説明します。

それは質問に答えるはずです、今は個人的なアドバイスです:

ほとんどの場合、アルゴリズムの動作を検証する場合(最初の成功で停止し、リストを逆の順序で処理する場合)を除いて、単体テストで期待値をテストしないでください。

あなたの例では、processメソッドは「物事を翻訳する」必要があります。したがって、単体テストはそれに焦点を当てる必要があります。パーサーとトランスレーターをモックし、それらをスタブし、プロセス全体の結果のみをチェックします。細かさは劣りますが、単体テストの目的は、メソッドのすべてのステップをチェックすることではありません。実装を変更する場合は、メソッドの各行を検証する一連の単体テストを変更する必要はありません。

于 2011-09-02T20:38:30.317 に答える
1

私はこれをなんとか解決できましたが、もっと良い解決策があるかもしれないので、私は自分の答えを投稿するつもりですが、すぐには受け入れません。

私がする必要があったのは、モックの適切なデフォルトの戻り値を、org.mockito.stubbing.Answer<T>Tが戻りタイプである形式で提供することでした。

私は次のモックセットアップでこれを行うことができました:

val defaultParseResult = new Answer[Option[MyType1]] {
  def answer(p1: InvocationOnMock): Option[MyType1] = None
}
val mockParser = org.mockito.Mockito.mock(implicitly[ClassManifest[Parser]].erasure,
                         defaultParseResult).asInstanceOf[Parser]

org.specs2.mock.Mockito特性とそれが呼び出すもののソースを少し閲覧した後。

そして今、nullを返す代わりに、解析はNoneスタブされていない場合(最初のテストのように予期されている場合を含む)に戻ります。これにより、テスト対象のコードで使用されているこの値でテストに合格できます。

mockParserこの一連のテストだけで複数のリターンタイプで同じ機能が必要になるため、割り当ての混乱を隠し、さまざまなリターンタイプで同じことを実行できるようにするテストサポートメソッドを作成する可能性があります。

でこれを行うためのより短い方法のサポートを見つけることができませんでしたorg.specs2.mock.Mockitoが、おそらくこれはエリックにそのようなものを追加するように促します。作者が会話に参加できてうれしいです...

編集

ソースをさらに詳しく調べてみると、メソッドを呼び出すことができるはずだと思いました。

def mock[T, A](implicit m: ClassManifest[T], a: org.mockito.stubbing.Answer[A]): T = org.mockito.Mockito.mock(implicitly[ClassManifest[T]].erasure, a).asInstanceOf[T]

で定義されてorg.specs2.mock.MockitoMockerいます。これは、実際には上記の私のソリューションのインスピレーションでした。しかし、私はその呼びかけを理解することができません。 mockかなり過負荷になっていて、すべての試みが別のバージョンを呼び出して、パラメーターが気に入らないように見えます。

したがって、エリックはすでにこれのサポートを含んでいるように見えますが、私はそれに到達する方法を理解していません。

アップデート

私は以下を含む特性を定義しました:

  def mock[T, A](implicit m: ClassManifest[T], default: A): T = {
    org.mockito.Mockito.mock(
      implicitly[ClassManifest[T]].erasure,
      new Answer[A] {
      def answer(p1: InvocationOnMock): A = default
    }).asInstanceOf[T]
  }

そして今、その特性を使用することで、モックを次のように設定できます

implicit val defaultParseResult = None
val mockParser = mock[Parser,Option[MyType1]]

この特定のテストでこれをさらに使用する必要はありません。これに使用可能な値を指定すると、テスト対象のコードでnullチェックなしですべてのテストが機能するためです。ただし、他のテストで必要になる場合があります。

この特性を追加せずにこの問題を処理する方法にまだ興味があります。

于 2011-09-03T13:44:42.223 に答える
0

完全にないと言うのは難しいですが、モックしようとしている方法が最終的な方法ではないことを確認してください。その場合、Mockitoはそれをモックできず、nullを返すためです。

何かが機能しない場合のもう1つのアドバイスは、標準のJUnitテストでMockitoを使用してコードを書き直すことです。その後、失敗した場合は、Mockitoメーリングリストの誰かがあなたの質問に答えるのが最善かもしれません。

于 2011-09-03T10:46:41.897 に答える