I have a class that has members injected through constructors, and OTHERS through setters. I can't seem to get Mockito to inject the setter ones. The constructor-injected are mocked fine, but the setter ones come back as null. When I flipped the setter-ed members to constructor- injected, all is well. here is the original production code:
@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
....
}
Test Code:
@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);
....
}
Any ideas?
THanks!
Yes, the @InjectMocks annotation makes Mockito EITHER do constructor injection, OR setter/field injection, but NEVER both. The rules around which will be chosen are quite complicated, which is one reason why I try to avoid using @InjectMocks whenever possible.
To summarise, Mockito FIRST chooses one constructor from among those that the class has, THEN analyses whether that constructor can be used for constructor injection. The one it chooses will always be the one with the greatest number of arguments. If there are several constructors with the same number of arguments, then it is undefined which one will be chosen.
Constructor injection will NOT be used if the type of one or more of the parameters of the CHOSEN constructor is a primitive type, or a final class or a private class. Even if there are other constructors that COULD be used.
If constructor injection is not used, or if the only constructor is the default constructor, then setter/field injection will be used instead. But setter/field injection is never used in conjunction with constructor injection.
Although Constructor injection is highly recommended, and I'd strongly advise against mixing injection methods, I've come across such a class which I can't refactor. To get around the problem, call initMocks
explicity. For example:
@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()));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With