Mockito injection not working for constructor AND setter mocks together

大城市里の小女人 提交于 2019-12-06 02:49:41

问题


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!


回答1:


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.




回答2:


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



回答3:


This is wanted by mockito framework. See this Github Issue.

I found out that it is possible to bypass this behaviour if you extend your testclass from any other class (i created an empty class with a meaningful name and a small comment).

I don't know exactly why but i belief that in case of a extended test class any internal routine of mockito will do the injection twice. Maybe because mockito goes throught the class hirachy and has a internal routine which injects throught constructor on the first time and second time he makes the setter/property injection. Just a guess. If i have time i will have a look into the mockito implementation.



来源:https://stackoverflow.com/questions/12668289/mockito-injection-not-working-for-constructor-and-setter-mocks-together

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!