12

コンストラクターを介してメンバーが注入され、セッターを介してOTHERSが注入されるクラスがあります。Mockitoにセッターを注入させることができないようです。コンストラクターによって注入されたものは正常にモックされますが、セッターのものはnullとして返されます。setter-edメンバーをコンストラクター注入に切り替えたとき、すべてが順調です。元の製品コードは次のとおりです。

@Autowired
private BetRepository betRepository;

public void setBetRepository(BetRepository betRepository) {
this.betRepository = betRepository;
}


public TournamentScoringCache(TournamentScoringCacheInitializer cacheInitializer,
        ScoringEngineInitializer scoringEngineInitializer) {
    tournamentUserStates = cacheInitializer.initCache();
    scoringEngines = scoringEngineInitializer.initEngines();
}

public <T extends SideScore> void updateGameScore(Long tournamentId, Long gameId, MatchScore<T> score) {
    Map<Long, UserTournamentState> userStates = tournamentUserStates.get(tournamentId);
    ScoringEngine<?> scoringEngine = scoringEngines.get(tournamentId);
    List<Bet> bets = betRepository.getBetsByGameId(gameId);  //HERE IS WHERE I GET THE NPE
....
}

テストコード:

@Mock
BetRepository betRepository;
@Mock
TournamentScoringCacheInitializer cacheInitializer;
@Mock
ScoringEngineInitializer engineInitializer;

@InjectMocks
private TournamentScoringCacheAndDB tournamentScoringCache;

@Test
public void testUpdateGameScore() {
....        
when(cacheInitializer.initCache()).thenReturn(utss);
    when(betRepository.getBetsByGameId(1L)).thenReturn(createBets());
    when(engineInitializer.initEngines()).thenReturn(createEngines());
    when(engine.getBetScore(bet1, score)).thenReturn(betScore);
    when(engine.getBetScore(bet2, score)).thenReturn(betScore2);

    tournamentScoringCache.updateGameScore(tournamentId, gameId, score);
....
}

何か案は?

ありがとう!

4

4 に答える 4

28

はい、@ InjectMocksアノテーションにより、Mockitoはコンストラクターインジェクション、またはセッター/フィールドインジェクションのいずれかを実行しますが、両方を実行することはできません。選択されるルールは非常に複雑です。これが、可能な限り@InjectMocksの使用を避けようとする理由の1つです。

要約すると、Mockito FIRSTは、クラスが持つコンストラクターから1つのコンストラクターを選択し、次に、そのコンストラクターをコンストラクターインジェクションに使用できるかどうかを分析します。選択するのは、常に引数の数が最も多いものです。同じ数の引数を持つコンストラクターが複数ある場合、どれが選択されるかは未定義です。

CHOSENコンストラクターの1つ以上のパラメーターの型がプリミティブ型、または最終クラスまたはプライベートクラスである場合、コンストラクターインジェクションは使用されません。使用できるコンストラクターが他にある場合でも。

コンストラクターインジェクションが使用されていない場合、またはコンストラクターがデフォルトのコンストラクターのみである場合は、代わりにセッター/フィールドインジェクションが使用されます。ただし、セッター/フィールドインジェクションをコンストラクターインジェクションと組み合わせて使用​​することはありません。

于 2012-10-01T06:58:48.940 に答える
17

コンストラクターインジェクションを強くお勧めします。インジェクションメソッドを混合しないことを強くお勧めしますが、リファクタリングできないようなクラスに出くわしました。この問題を回避するには、明示的にを呼び出しますinitMocks。例えば:

@InjectMocks
private ThingWithMixedDependencies thing;

@Mock
private FieldInjected secondDependency;

@BeforeEach
void setUp() {
  // This cannot be a mocked field or else it will reinitialise thing.
  ConstructorInjected firstDependency = Mockito.mock(ConstructorInjected.class);
  thing = new ThingWithMixedDependencies(firstDependency);
  MockitoAnnotations.initMocks(this);
}

@Test
void checkDependencies() {
  assertThat(thing.getFirstDependency(), is(notNullValue()));
  assertThat(thing.getSecondDependency(), is(notNullValue()));
}
于 2018-07-12T12:04:13.850 に答える
1

これはmockitoフレームワークで必要とされています。このGithubの問題を参照してください。

テストクラスを他のクラスから拡張すると、この動作を回避できることがわかりました(意味のある名前と小さなコメントを持つ空のクラスを作成しました)。

正確な理由はわかりませんが、拡張テストクラスの場合、mockitoの内部ルーチンで2回注入が行われると思います。おそらく、mockitoはクラスhirachyを通過し、最初と2回目にsetter / propertyインジェクションを行うときに、コンストラクターを介してインジェクトする内部ルーチンを持っているためです。ただの推測。時間があれば、mockitoの実装を調べます。

于 2018-03-04T18:07:07.787 に答える
-1

この場合の最も簡単な修正はを使用することです。これにより、コンストラクタベースのモックとセッターベースのモックの両方が注入されます。

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }
于 2020-11-25T15:03:29.000 に答える