web-dev-qa-db-ja.com

mockitoを使用して文字列をモックする方法は?

StringオブジェクトのgetBytes()メソッドを呼び出すと、UnsupportedEncodingExceptionが発生するテストシナリオをシミュレートする必要があります。

私は次のコードを使用してそれを達成しようとしました:

String nonEncodedString = mock(String.class);
when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));

問題は、テストケースを実行すると、Java.lang.StringクラスをモックできないというMockitoExceptionが発生することです。

Mockitoを使用してStringオブジェクトをモックする方法、またはgetBytesメソッドを呼び出すときにStringオブジェクトにUnsupportedEncodingExceptionをスローさせる方法はありますか?


問題を説明するための詳細を次に示します。

これは私がテストしたいクラスです:

public final class A {
    public static String f(String str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

これは私のテストクラスです(JUnit 4とmockitoを使用しています)。

public class TestA {

    @Test(expected=UnsupportedEncodingException.class)
    public void test(){
        String aString = mock(String.class);
        when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));
        A.f(aString);
    }
}
38
Alceu Costa

エンコード名が正しくないStringを作成するのはどうですか?見る

public String(byte bytes[], int offset, int length, String charsetName)

Stringのモックは、ほぼ間違いなく悪い考えです。

13
Steve Freeman

Catchブロックで実行するのが実行時例外をスローするだけである場合は、Charsetオブジェクトを使用して文字セット名を指定するだけで入力を節約できます。

public final class A{
    public static String f(String str){
        return new String(str.getBytes(Charset.forName("UTF-8")));
    }
}

この方法では、コンパイラから指示されたからといって発生することのない例外をキャッチしていません。

13
Martin Hilton

他の人が示したように、Mockitoを使用して最終クラスをモックすることはできません。ただし、より重要な点は、String.getBytes()が例外をスローできることを示しているだけなので、テストは特に有用ではないということです。この機能のテストについて強く感じている場合は、f()にエンコードのパラメーターを追加し、テストに不適切な値を送信できると思います。

また、Aが最終であり、A.f()が静的であるため、f()の呼び出し元に対して同じ問題を引き起こしています。

この記事は、同僚に100%のコードカバレッジについて独断的にならないように説得するのに役立つ場合があります。 100%のテストカバレッジで失敗する方法

7
Jason

そのドキュメントから、JDaveはbootstrapクラスローダー。すべてのJREクラス(Java.lang、Java.utilなどから)を含む)によってロードされたクラスから「最終」修飾子を削除できません。

何でもモックできるツールは JMockit です。

JMockitを使用すると、テストは次のように記述できます。

_import Java.io.*;
import org.junit.*;
import mockit.*;

public final class ATest
{
   @Test(expected = UnsupportedOperationException.class)
   public void test() throws Exception
   {
      new Expectations()
      {
         @Mocked("getBytes")
         String aString;

         {
            aString.getBytes(anyString);
            result = new UnsupportedEncodingException("Parsing error.");
         }
      };

      A.f("test");
   }
}
_

完全な「A」クラスは次のようになります。

_import Java.io.*;

public final class A
{
   public static String f(String str)
   {
      try {
         return new String(str.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException e) {
         throw new UnsupportedOperationException(e);
      }
   }
}
_

実際にマシンでこのテストを実行しました。 (元のチェック済み例外をランタイム例外でラップしたことに注意してください。)

JMockitが_Java.lang.String_クラスのすべてをモックするのを防ぐために、@Mocked("getBytes")を介した部分モックを使用しました(それが原因で何が起こるか想像してください)。

「UTF-8」は標準の文字セットであり、すべてのJREでサポートする必要があるため、このテストは本当に不要です。したがって、実稼働環境では、catchブロックは実行されません。

ただし、catchブロックをカバーする「必要性」または要望は依然として有効です。それでは、カバレッジ率を低下させずにテストを取り除く方法は?私のアイデアは次のとおりです。最初のステートメントとして_assert false;_をキャッチブロック内に挿入し、カバレッジメジャーをレポートするときにコードカバレッジツールでキャッチブロック全体を無視します。これは、JMockit Coverageの「TODOアイテム」の1つです。 8 ^)

5
Rogério

実行できないコードをテストします。 UTF-8サポートは、すべてのJava VM、 http://Java.Sun.com/javase/6/docs/api/Java/nio/charsetを参照してください。 /Charset.html

3
Thomas Lötzer

Mockitoは最終クラスをモックできません。 JMaveとJDaveのライブラリを組み合わせたもの。 手順はこちら

JMockは、JDのすべてのファイナライズを解除するためにJDaveライブラリに依存すること以外は、ファイナルクラスに対して特別なことはしません。

3
Yishai

また、PowerMockのMockito拡張機能を使用して、Stringなどのシステムクラスであっても最終クラス/メソッドをモックできます。ただし、この場合はgetBytesをモックすることもお勧めします。代わりに、実際の文字列が、予想されるデータが入力されたStringが代わりに使用されるように、予想を設定します。

3
Johan

おそらく、A.f(String)は代わりにA.f(CharSequence)である必要があります。 CharSequenceをモックできます。

1
Chris Martin

ユニットテストのカバレッジ率は、指定された値よりも高くなければならないことがプロジェクト要件です。このようなカバレッジの割合を達成するには、テストはUnsupportedEncodingExceptionに関連するcatchブロックをカバーする必要があります。

与えられたカバレッジターゲットとは何ですか?一部の人々は、 100%のカバレッジを狙って撮影することは常に良い考えとは限りません と言うでしょう。

それに、キャッチブロックが実行されたかどうかをテストする方法はありません。正しい方法は、例外をスローするメソッドを記述し、スローされている例外を成功基準で観察することです。 「期待される」値を追加することにより、JUnitの@Testアノテーションでこれを行います。

@Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() {
   new ArrayList<Object>().get(1);
}
1
duffymo

無効なcharsetNameをgetBytes(String)に渡そうとしましたか?

ヘルパーメソッドを実装してcharsetNameを取得し、テスト内でそのメソッドを無効な値にオーバーライドできます。

1
Rich Seller

インターフェイスCharSequenceを取るようにメソッドを変更できます:

public final class A {
    public static String f(CharSequence str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

そうすれば、引き続きStringを渡すことができますが、好きなようにモックできます。

0
tkruse

JMockitを使用できる場合は、Rogérioの回答をご覧ください。

コードカバレッジを取得するが、実行時にUTF-8の欠落がどのように見えるかを実際にシミュレートしないことが目標である場合にのみ、次のことができます(JMockitを使用できない、または使用したくない)

public static String f(String str){
    return f(str, "UTF-8");
}

// package private for example
static String f(String str, String charsetName){
    try {
        return new String(str.getBytes(charsetName));
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
    }
}

public class TestA {

    @Test(expected=IllegalArgumentException.class)
    public void testInvalid(){
        A.f(str, "This is not the encoding you are looking for!");
    }

    @Test
    public void testNormal(){
        // TODO do the normal tests with the method taking only 1 parameter
    }
}
0