Mockito:モック専用フィールドの初期化


95

インラインで初期化されているフィールド変数をモックするにはどうすればよいですか?

class Test {
    private Person person = new Person();
    ...
    public void testMethod() {
        person.someMethod();
        ...
    }
}

ここで、変数の初期化をモックする必要のperson.someMethod()あるTest.testMethod()メソッドをテストしながらモックしたいと思いpersonます。どんな手掛かり?

編集: Personクラスの変更は許可されていません。


1
このリンクは、あなたに役立つかもしれないstackoverflow.com/questions/13645571/...
ポパイ

2
のモックを渡すことができるように、コードをリファクタリングする必要がありますPerson。オプションには、これを行うためのコンストラクターの追加、またはセッターメソッドの追加が含まれます。
Tim Biegeleisen

回答:


109

Mockitoには、反射ボイラープレートコードを保存するヘルパークラスが付属しています。

import org.mockito.internal.util.reflection.Whitebox;

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    Whitebox.setInternalState(underTest, "person", mockedPerson);
    // ...
}

更新: 残念ながら、mockitoチームはMockito 2 のクラス削除することを決定しました。これで、独自のリフレクションボイラープレートコードの記述に戻るか、別のライブラリ(Apache Commons Langなど)を使用するか、単にホワイトボックスクラスを盗みます(MITライセンスです)。

更新2: JUnit 5には、便利な独自のReflectionSupportクラスとAnnotationSupportクラスが付属しており、これらを使用すると、さらに別のライブラリを取得する必要がなくなります。


他の理由でターゲットオブジェクトをスパイしています。この場合、オブジェクトがスパイの場合、内部状態をこのように設定できません。
2016年

何故なの?スパイと一緒に使っています。Personインスタンスを作成します。スタブが必要なものをスタブし、それをTestインスタンスに設定します。
ラルフ

警告:ホワイトボックスはinternalパッケージに含まれており、Mockito 2.6.2では動作しないようです。
ノヴァ

FieldSetter.setField()を使用できます。同じことについて、以下の例を挙げました。
Raj Kumar

1
@Ralf Javaで参照名を変更すると、常にコンパイルエラーが発生するためです。
Joe Coder

69

パーティーにはかなり遅れましたが、私はここで打たれ、友人から助けを得ました。問題は、PowerMockを使用しないことでした。これはMockitoの最新バージョンで動作します。

Mockitoにはこれが付属していorg.mockito.internal.util.reflection.FieldSetterます。

基本的には、リフレクションを使用してプライベートフィールドを変更するのに役立ちます。

これはあなたがそれを使う方法です:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
    // ...
    verify(mockedPerson).someMethod();
}

このようにして、モックオブジェクトを渡して、後で検証することができます。

こちらが参考です。


FieldSetterMockito 2.xでは使用できなくなりました。
ラルフ

4
@Ralf私はバージョンmockito-core-2.15.0を使用しています。そこで利用できます。static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/…。まだベータ版です。
Raj Kumar

1
@RajKumar Class#getDeclaredFieldは単一のパラメータを受け入れるため、括弧はこのようにする必要がありますFieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);。それが私がそれを誤解した方法であり、あなたの例でそれを修正するのは良い考えかもしれないと思いました。返信いただきありがとうございます。
Dapeng Li

1
内部APIを使用することは最良のアイデアではありません
David

@RajKumarいいですが、Zimbo Rodgerが述べたように:内部APIを使用するのは良い考えですか?なぜそれが実際に内部にあるのですか?テストにはとても便利です。
Willi Mentzel

32

Spring Testを使用する場合は、org.springframework.test.util.ReflectionTestUtilsを試してください。

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);

1
すごい!この記事にリンクし、必要な依存関係を含めるのに役立つ
Willi Mentzel

build.gradle.kts:testImplementation("org.springframework:spring-test:5.1.2.RELEASE")
Willi Mentzel

28

私はここに投稿するのを忘れていたこの問題の解決策をすでに見つけました。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
    Test test= new Test();
    test.testMethod();
    }
}

このソリューションの重要なポイントは次のとおりです。

  1. PowerMockRunnerでテストケースを実行する: @RunWith(PowerMockRunner.class)

  2. Test.classプライベートフィールドの操作を準備するようにPowermockに指示します。@PrepareForTest({ Test.class })

  3. そして最後にPersonクラスのコンストラクタをモックします:

    PowerMockito.mockStatic(Person.class); PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);


8
説明ではmockStatic関数について説明していますが、コード例では説明していません。コード例にmockStatic呼び出しがあるべきですか、それともコンストラクターには必要ありませんか?
Shadoninja

10

RESTクライアントモックでマッパーを初期化するには、次のコードを使用できます。mapperフィールドはプライベートであり、ユニットテストのセットアップ時に設定する必要があります。

import org.mockito.internal.util.reflection.FieldSetter;

new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());

5

すべてのテストで変数に同じ値を設定する必要がある場合は、@ Jardaのガイドを使用してこれを定義できます。

@Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
    FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}

ただし、プライベート値を異なる値に設定する場合は注意して処理する必要があることに注意してください。それらがプライベートである場合は、何らかの理由があります。

たとえば、ユニットテストでスリープの待機時間を変更する場合などに使用します。実際の例では10秒間スリープしたいのですが、単体テストでは即時であれば十分です。統合テストでは、実際の値をテストする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.