315

次のような最終クラスがあります。

public final class RainOnTrees{

   public void startRain(){

        // some code here
   }
}

私はこのクラスを次のような他のクラスで使用しています:

public class Seasons{

   RainOnTrees rain = new RainOnTrees();

   public void findSeasonAndRain(){

        rain.startRain();

    }
}

私のJUnitテストクラスではSeasons.java、クラスをモックしたいと考えていRainOnTreesます。Mockitoでこれを行うにはどうすればよいですか?

4

28 に答える 28

259

Mockito 2 が finalクラスとメソッドをサポートするようになりました!

しかし今のところ、それは「インキュベーション中」の機能です。What's New in Mockito 2 で説明されているように、アクティブ化するにはいくつかの手順が必要です。

final クラスとメソッドのモックは、準備中のオプトイン機能です。これらのタイプのモック可能性を有効にするために、Java エージェントの計測とサブクラス化の組み合わせを使用します。これは現在のメカニズムとは異なる動作をし、これには異なる制限があり、経験とユーザーからのフィードバックを収集したいため、この機能を利用できるようにするには明示的に有効にする必要がありました。src/test/resources/mockito-extensions/org.mockito.plugins.MockMakerこれは、1 行を含むファイルを作成することにより、mockito 拡張メカニズムを介して実行できます。

mock-maker-inline

このファイルを作成すると、Mockito は自動的にこの新しいエンジンを使用し、次のことが可能になります。

 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());

以降のマイルストーンでは、チームはこの機能をプログラムで使用する方法を提供します。すべてのモック不可能なシナリオを特定し、サポートを提供します。この機能についてのご意見をお聞かせください。

于 2016-10-13T10:19:39.907 に答える
247

最終/静的クラス/メソッドのモックは、Mockito v2 でのみ可能です。

これを gradle ファイルに追加します。

testImplementation 'org.mockito:mockito-inline:2.13.0'

これは、Mockito FAQからの Mockito v1 では不可能です。

Mockitoの制限は何ですか

  • Java 1.5 以降が必要

  • 最終クラスをモックできません

...

于 2013-01-12T11:26:14.870 に答える
44

自分ではできないので、Mockitoで最終クラスをモックすることはできません。

私がやっていることは、最終クラスをラップしてデリゲートとして使用する非最終クラスを作成することです。この例はTwitterFactoryクラスであり、これは私のモック可能なクラスです。

public class TwitterFactory {

    private final twitter4j.TwitterFactory factory;

    public TwitterFactory() {
        factory = new twitter4j.TwitterFactory();
    }

    public Twitter getInstance(User user) {
        return factory.getInstance(accessToken(user));
    }

    private AccessToken accessToken(User user) {
        return new AccessToken(user.getAccessToken(), user.getAccessTokenSecret());
    }

    public Twitter getInstance() {
        return factory.getInstance();
    }
}

欠点は、定型コードがたくさんあることです。利点は、アプリケーションビジネスに関連する可能性のあるいくつかのメソッドを追加できることです(上記の場合、accessTokenの代わりにユーザーを取得するgetInstanceなど)。

あなたの場合、私は最終クラスに委任する非最終RainOnTreesクラスを作成します。または、それを非決勝にすることができれば、それはより良いでしょう。

于 2013-01-12T11:39:55.103 に答える
25

パワーモックを使用します。このリンクは、その方法を示しています: https://github.com/jayway/powermock/wiki/MockFinal

于 2013-01-13T20:07:16.233 に答える
12

final他のクラスが拡張されないようにしたいので作成したと思いますRainOnTrees効果的なJavaが示唆しているように(アイテム15)、クラスを作成せずに拡張のためにクラスを閉じておく別の方法がありますfinal

  1. finalキーワードを削除します。

  2. そのコンストラクターを作成しprivateます。superコンストラクターを呼び出すことができないため、どのクラスもそれを拡張できません。

  3. クラスをインスタンス化する静的ファクトリ メソッドを作成します。

    // No more final keyword here.
    public class RainOnTrees {
    
        public static RainOnTrees newInstance() {
            return new RainOnTrees();
        }
    
    
        private RainOnTrees() {
            // Private constructor.
        }
    
        public void startRain() {
    
            // some code here
        }
    }
    

この戦略を使用することで、Mockito を使用して、定型コードをほとんど使用せずにクラスを拡張用に閉じたままにすることができます。

于 2016-07-14T21:47:26.443 に答える
11

私も同じ問題を抱えていました。モックしようとしていたクラスは単純なクラスだったので、単純にそのインスタンスを作成して返しました。

于 2013-12-10T11:35:22.017 に答える
6

場合によっては適用される可能性のある別の回避策は、その最終クラスによって実装されるインターフェイスを作成し、具象クラスの代わりにインターフェイスを使用するようにコードを変更してから、インターフェイスをモックすることです。これにより、コントラクト (インターフェイス) を実装 (最終クラス) から分離できます。もちろん、最終クラスにバインドすることが本当に必要な場合は、これは当てはまりません。

于 2015-02-02T15:46:39.230 に答える
5

実際には、スパイに使用する方法が 1 つあります。次の 2 つの前提条件が満たされている場合にのみ機能します。

  1. ある種のDIを使用して、最終クラスのインスタンスを注入します
  2. 最終クラスはインターフェースを実装します

有効な Javaの項目 16 を思い出してください。ラッパー (final ではない) を作成し、すべての呼び出しを final クラスのインスタンスに転送できます。

public final class RainOnTrees implement IRainOnTrees {
    @Override public void startRain() { // some code here }
}

public class RainOnTreesWrapper implement IRainOnTrees {
    private IRainOnTrees delegate;
    public RainOnTreesWrapper(IRainOnTrees delegate) {this.delegate = delegate;}
    @Override public void startRain() { delegate.startRain(); }
}

これで、最終クラスをモックするだけでなく、それをスパイすることもできます。

public class Seasons{
    RainOnTrees rain;
    public Seasons(IRainOnTrees rain) { this.rain = rain; };
    public void findSeasonAndRain(){
        rain.startRain();
   }
}

IRainOnTrees rain = spy(new RainOnTreesWrapper(new RainOnTrees()) // or mock(IRainOnTrees.class)
doNothing().when(rain).startRain();
new Seasons(rain).findSeasonAndRain();
于 2016-08-24T16:06:24.870 に答える
5

これを試してください:

Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable));

それは私のために働いた。「SomeMockableType.class」は、モックまたはスパイしたいものの親クラスであり、 someInstanceThatIsNotMockableOrSpyable は、モックまたはスパイしたい実際のクラスです。

詳細については、こちらをご覧ください

于 2014-11-20T12:17:34.307 に答える
4

これは、最終クラスとメソッドのモックをサポートする新しいインキュベーション機能を備えた Mockito2 を使用している場合に実行できます。

重要なポイント:
1. 「org.mockito.plugins.MockMaker」という名前の単純なファイルを作成し、「mockito-extensions」という名前のフォルダーに配置します。このフォルダーは、クラスパスで使用できるようにする必要があります。
2. 上記で作成したファイルの内容は、次のように 1 行である必要があります

上記の 2 つの手順は、mockito 拡張メカニズムを有効にしてこのオプトイン機能を使用するために必要です。

サンプル クラスは次のとおりです。

FinalClass.java

public final class FinalClass {

public final String hello(){
    System.out.println("Final class says Hello!!!");
    return "0";
}

}

Foo.java

public class Foo {

public String executeFinal(FinalClass finalClass){
    return finalClass.hello();
}

}

FooTest.java

public class FooTest {

@Test
public void testFinalClass(){
    // Instantiate the class under test.
    Foo foo = new Foo();

    // Instantiate the external dependency
    FinalClass realFinalClass = new FinalClass();

    // Create mock object for the final class. 
    FinalClass mockedFinalClass = mock(FinalClass.class);

    // Provide stub for mocked object.
    when(mockedFinalClass.hello()).thenReturn("1");

    // assert
    assertEquals("0", foo.executeFinal(realFinalClass));
    assertEquals("1", foo.executeFinal(mockedFinalClass));

}

}

それが役に立てば幸い。

ここにある完全な記事は、mocking-the-unmockableです。

于 2017-02-05T10:22:14.227 に答える
3

はい、ここでも同じ問題があります。Mockito で最終クラスをモックすることはできません。正確に言うと、Mockito は以下をモック/スパイできません。

  • 最終クラス
  • 匿名クラス
  • プリミティブ型

しかし、ラッパー クラスを使用すると大きな代償を払うように思えるので、代わりに PowerMockito を入手してください。

于 2014-07-18T03:19:19.063 に答える
1

JMockitを見てください。多くの例を含む広範なドキュメントがあります。ここにあなたの問題の解決策の例があります(簡単にするために、コンストラクターを追加して、モックされたインスタンスSeasonsを注入しました):RainOnTrees

package jmockitexample;

import mockit.Mocked;
import mockit.Verifications;
import mockit.integration.junit4.JMockit;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(JMockit.class)
public class SeasonsTest {

    @Test
    public void shouldStartRain(@Mocked final RainOnTrees rain) {
        Seasons seasons = new Seasons(rain);

        seasons.findSeasonAndRain();

        new Verifications() {{
            rain.startRain();
        }};
    }

    public final class RainOnTrees {
        public void startRain() {
            // some code here
        }

    }

    public class Seasons {

        private final RainOnTrees rain;

        public Seasons(RainOnTrees rain) {
            this.rain = rain;
        }

        public void findSeasonAndRain() {
            rain.startRain();
        }

    }
}
于 2014-10-31T20:46:41.860 に答える
1

testフォルダーの下で unit-test を実行しようとしている場合は、一番上のソリューションで問題ありません。拡張子を追加するだけです。

しかし、 androidtestフォルダーの下にある context や activity などのandroid 関連のクラスで実行したい場合は、答えはあなたのためです。

于 2018-12-05T21:56:42.347 に答える
1

RC と Luigi R. Viggiano が一緒に提供するソリューションは、おそらく最良のアイデアです。

Mockitoは設計上、最終クラスをモックすることはできませんが、委任アプローチは可能です。これには次の利点があります。

  1. API がそもそもそれを意図している場合は、クラスを非 final に変更する必要はありません (final クラスには利点があります)。
  2. APIの装飾の可能性をテストしています。

テスト ケースでは、呼び出しを意図的にテスト中のシステムに転送します。したがって、設計上、装飾は何もしません

したがって、ユーザーが API を拡張するのではなく装飾することしかできないことをテストすることもできます。

より主観的なメモ: 私はフレームワークを最小限に抑えることを好みます。そのため、JUnit と Mockito で通常は十分です。実際、この方法を制限すると、完全にリファクタリングしなければならないこともあります。

于 2015-12-08T21:59:31.480 に答える
0

他の人が述べているように、これは Mockito ではそのままでは機能しません。リフレクションを使用して、テスト対象のコードで使用されているオブジェクトに特定のフィールドを設定することをお勧めします。これを頻繁に行う場合は、この機能をライブラリにラップできます。

余談ですが、あなたがクラスを最終的にマークしている場合は、それをやめてください。この質問に出くわしたのは、拡張 (モック) の正当な必要性を防ぐためにすべてが final とマークされた API を使用しており、開発者がクラスを拡張する必要がないと想定していなかったことを願っています。

于 2017-01-05T13:54:49.727 に答える