Mockito:実際のオブジェクトをプライベート@Autowiredフィールドに挿入する


190

私はMockito @Mock@InjectMocksアノテーションを使用して、Springでアノテーションが付けられたプライベートフィールドに依存関係を挿入しています@Autowired

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

そして

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

次に、実際のオブジェクトもプライベート@Autowiredフィールド(セッターなし)に挿入したいと思います。これは可能ですか、それともモックの注入のみに制限されていますか?


5
通常、物事をあざけるときは、具象オブジェクトについてあまり気にしないことを意味します。あなたは本当にモックされたオブジェクトの振る舞いだけに関心があるということです。おそらく、代わりに統合テストを行いたいですか?または、モック化されたコンクリートのオブジェクトが一緒に暮らしたい理由についての根拠を教えていただけませんか?

2
さて、私はレガシーコードを扱っており、NPEなどを防ぐためだけにモックをセットアップするには、when(...)。thenReturn(...)ステートメントがたくさん必要になります。一方、実際のオブジェクトはこのために安全に使用できます。したがって、モックとともに実際のオブジェクトを注入するオプションがあると非常に便利です。これはコードのにおいかもしれませんが、この特定のケースではそれは妥当だと思います。
user2286693 2013年

忘れないようにMockitoAnnotations.initMocks(this);して@Before方法。私はそれが元の質問に直接関連していないことを知っていますが、後で実行する人には、これを実行可能にするために追加する必要があります。
Cuga

7
@Cuga:JUnitにMockitoランナーを使用する場合(@RunWith(MockitoJUnitRunner.class))、ラインは必要ありませんMockitoAnnotations.initMocks(this);
Clint Eastwood

1
ありがとう-私はそれを知らなかったし、常に両方を指定してきた
Cuga

回答:


304

@Spy注釈を使用

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockitoは@Mock@Spyアノテーションまたはアノテーションが付いたすべてのフィールドを、アノテーションが付いたインスタンスに挿入される潜在的な候補と見なします@InjectMocks。上記の場合、'RealServiceImpl'インスタンスは「デモ」に挿入されます

詳細については参照してください

モッキトホーム

@スパイ

@モック


9
+1:私のために働いた... Stringオブジェクトを除いて。Mockitoは不平を言います:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
エイドリアンPronk

おかげで私にとっても
うまくいきました

ありがとう!それがまさに私が必要としていたことです!
nterry、2015

2
私の場合、Mockitoはスパイを注入していません。モックを注入します。フィールドはプライベートで、セッターはありません。
Vituel

8
ところで、する必要がないnew RealServiceImpl()@Spy private SomeService service;とにかくそれをスパイする前に、デフォルトコンストラクタを使用して実際のオブジェクトを作成します。
parxier 2017

20

@Devに加えて、空白の回答で、Springによって作成された既存のBeanを使用する場合は、コードを次のように変更できます。

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

この方法では、テストを機能させるためだけにコードを変更(別のコンストラクターを追加)する必要はありません。


2
@Aada詳しく説明していただけますか?
Yoaz Menda

1
これはどのライブラリからのものですか、InjectMocksはorg.mockitoでしか表示されません
Sameer

1
@sameer import org.mockito.InjectMocks;
Yoaz Menda

コメントを追加してAadaをキャンセルします。これでうまくいきました。他の回答で提案されているように、「フィールドインジェクションを単純に使用しない」ことができなかったためAutowired、依存関係のフィールドが正しく作成されていることを確認する必要がありました。
スクランボ

1
私はこれを試しました。しかし、セットアップメソッドからnullポインタ例外が発生します
Akhil Surapuram

3

MockitoはDIフレームワークではなく、DIフレームワークでさえ、フィールドインジェクションよりもコンストラクタインジェクションを奨励します。
したがって、テストするクラスの依存関係を設定するコンストラクタを宣言するだけです。

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

spy一般的なケースでMockito を使用するのはひどいアドバイスです。それはテストクラスをもろくし、まっすぐでエラーを起こしやすくしません。実際に何がテストされていますか?
@InjectMocksそして@Spy、それはクラスで肥大化したクラスや混合責任を奨励しているので、全体的なデザインを痛いです。盲目的に使用する前javadocを読んで
ください(強調は私のものではありません)。spy()

実際のオブジェクトのスパイを作成します。スパイは、スタブされていない限り、実際のメソッドを呼び出します。実際のスパイは、たとえばレガシーコードを扱う場合など慎重に、そして時折使用する必要があります。

いつものように、partial mock warningオブジェクト指向プログラミングは複雑さを個別の特定のSRPyオブジェクトに分割することで複雑さに取り組みます。部分モックはどのようにこのパラダイムに適合しますか?まあ、そうではありません...部分的なモックは通常、複雑さが同じオブジェクトの別のメソッドに移動されたことを意味します。ほとんどの場合、これはアプリケーションの設計方法ではありません。

ただし、部分モックが便利な場合はまれにあります。簡単に変更できないコードを処理する(サードパーティのインターフェイス、レガシーコードの暫定的なリファクタリングなど)。ただし、部分的なモックは、新しいテスト駆動型で十分に使用しません。設計されたコード。


0

Springには、ReflectionTestUtilsこの目的のために呼び出される専用のユーティリティがあります。特定のインスタンスを取得して、フィールドに注入します。


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}

-1

これは古い質問であることは知っていますが、文字列を注入しようとしたときに同じ問題に直面しました。したがって、私たちはあなたが望むことを正確に実行するJUnit5 / Mockito拡張を発明しました:https : //github.com/exabrial/mockito-object-injection

編集:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.