Mockito注入不适用于构造函数和setter模拟在一起

时间:2012-10-01 06:12:36

标签: spring constructor code-injection mockito setter

我有一个类通过构造函数注入成员,而OTHERS通过setter注入。我似乎无法让Mockito注入二传手。注入的构造函数被模拟得很好,但是setter的返回为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 个答案:

答案 0 :(得分:14)

是的,@ InjectMocks注释使Mockito EITHER进行构造函数注入,或者setter / field注入,但是从不这两个。选择的规则非常复杂,这也是我尽量避免使用@InjectMocks的原因之一。

总而言之,Mockito FIRST从类中包含的构造函数中选择一个构造函数,然后分析该构造函数是否可用于构造函数注入。它选择的那个将永远是具有最多参数的那个。如果有几个构造函数具有相同数量的参数,则未定义哪个将被选择。

如果CHOSEN构造函数的一个或多个参数的类型是基本类型,或者是最终类或私有类,则不会使用构造函数注入。即使有其他构造函数可以使用。

如果未使用构造函数注入,或者唯一的构造函数是默认构造函数,则将使用setter / field injection。但是setter / field injection永远不会与构造函数注入一起使用。

答案 1 :(得分:3)

尽管强烈建议使用构造函数注入,并且我强烈建议不要使用混合注入方法,但是我遇到了无法重构的此类。要解决此问题,请call initMocks explicity。例如:

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

答案 2 :(得分:1)

mockito框架需要这个。请参阅此Github Issue

我发现如果你从任何其他类扩展你的testclass,我可以绕过这种行为(我创建了一个带有意义名称和小注释的空类)。

我不确切知道为什么,但我相信,如果是扩展测试类,任何mockito的内部例程都会进行两次注射。也许是因为mockito经历了类hirachy并且有一个内部例程,它在第一次注入构造函数,第二次注入setter / property。只是一个猜测。如果我有时间,我将看看mockito实现。

答案 3 :(得分:0)

在这种情况下,最简单的解决方法是使用。这将注入基于构造函数的模拟以及基于setter的模拟:

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
  }