12

我有一个类,它的成员通过构造函数注入,其他成员通过设置器注入。我似乎无法让 Mockito 注入二传手。构造函数注入被模拟得很好,但设置器返回为空。当我将 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 要么进行构造函数注入,要么进行 setter/field 注入,但从不两者兼而有之。选择的规则非常复杂,这也是我尽可能避免使用@InjectMocks 的原因之一。

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

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

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

于 2012-10-01T06:58:48.940 回答
17

尽管强烈建议使用构造函数注入,并且我强烈建议不要混合注入方法,但我遇到过这样一个我无法重构的类。要解决此问题,请调用initMocksexplicity。例如:

@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 遍历类 hiracy 并且有一个内部例程,该例程在第一次和第二次注入构造函数时,他进行了 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 回答