web-dev-qa-db-ja.com

最終メソッドのモック

Mockitoを使用してfinalメソッドでクラスを模擬する必要があります。私はこのようなものを書きました

@Test
public void test() {
    B b = mock(B.class);
    doReturn("bar called").when(b).bar();   
    assertEquals("must be \"overrided\"", "bar called", b.bar());
    //bla-bla
}


class B {
    public final String bar() {
        return "fail";
    }
}

しかし、失敗します。私はいくつかの「ハック」を試みましたが、うまくいきます。

   @Test
   public void hackTest() {
        class NewB extends B {
            public String barForTest() {
                return bar();
            }
        }
        NewB b = mock(NewB.class);
        doReturn("bar called").when(b).barForTest();
        assertEquals("must be \"overrided\"", "bar called", b.barForTest());
    }

動作しますが、「臭い」がします。

だから、正しい方法はどこですか?

ありがとう。

57
Stan Kurilin

Mockitoでは、最終メソッドのモック化はサポートされていません。

Jon Skeetがコメントしたように、finalメソッドへの依存を回避する方法を探す必要があります。とはいえ、バイトコード操作にはいくつかの方法があります(PowerMockなど)

MockitoとPowerMockの比較 で詳細を説明します。

29
iwein

Mockito FAQ から:

Mockitoの制限は何ですか

  • Finalメソッドをモックできません-それらの実際の動作は例外なく実行されます。 Mockitoは最終メソッドのモックについて警告することはできませんので注意してください。
32
matt b

PowermockをMockitoと一緒に使用すると、B.classをサブクラス化する必要がなくなります。これをテストクラスの先頭に追加するだけです

@RunWith(PowerMockRunner.class)
@PrepareForTest(B.class)

@PrepareForTestは、PowermockにB.classをインスツルメントして、finalメソッドとstaticメソッドをモック可能にするよう指示します。このアプローチの欠点は、Springテストランナーなどの他のテストランナーを使用できないPowerMockRunnerを使用する必要があることです。

20
Justin Rowe

Mockito 2は現在、最終メソッドのモッキングをサポートしていますが、これは「インキュベーション」機能です。ここで説明されているアクティベーションにはいくつかの手順が必要です: https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-最終クラスのモックメソッド

14
WindRider

Mockito 2.xは、finalメソッドとfinalクラスのスタブをサポートするようになりました。

ドキュメントから

最終的なクラスとメソッドのモックは、魅力的なオプトイン機能です。この機能は、ファイルsrc/test/resources/mockito-extensions/org.mockito.plugins.MockMaker単一行を含む:

mock-maker-inline

このファイルを作成したら、次のことができます。

final class FinalClass {
  final String finalMethod() { return "something"; }
}

FinalClass concrete = new FinalClass(); 

FinalClass mock = mock(FinalClass.class);
given(mock.finalMethod()).willReturn("not anymore");

assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());

その後のマイルストーンで、チームはこの機能をプログラムで使用する方法を提供します。すべての模擬不可能なシナリオを特定し、サポートを提供します。

5

Bクラスが次のようになっていると仮定します。

class B {
    private String barValue;
    public final String bar() {
        return barValue;
    }
    public void final setBar(String barValue) {
        this.barValue = barValue;
    }
}

PowerMockitoフレームワークを使用せずにこれを行うより良い方法があります。クラスのSPYを作成し、最終メソッドをモックできます。以下がその方法です。

@Test
public void test() {

    B b  = new B();
    b.setBar("bar called") //This should the expected output:final_method_bar()
    B spyB = Mockito.spy(b);
    assertEquals("bar called", spyB.bar());

}
3

私も同じことをしました。私の場合は、メソッドがエラーを「引き起こさない」ようにしたかったのですが、catch/log/returnメソッドなので、クラスを変更しないと直接テストできませんでした。

渡されたロガーを単純にモックしたかったのですが、「Log」インターフェースをモックすることはうまくいかないようで、「SimpleLog」のようなクラスをモックすることはそれらのメソッドが最終的なため機能しませんでした。

最終的に、他のすべてが委任するベースレベルの「log(level、string、error)」メソッドをオーバーライドするSimpleLogを拡張する匿名内部クラスを作成し、「レベル」が5の呼び出しを待機しました。

一般に、振る舞いのためにクラスを拡張することは実際には悪い考えではありません。複雑でなくても、とにかくing笑するよりも望ましいかもしれません。

0
Bill K