【问题标题】:Mockito injection not working for constructor AND setter mocks togetherMockito 注入不适用于构造函数和 setter 一起模拟
【发布时间】:2012-10-01 06:12:35
【问题描述】:

我有一个类,它的成员通过构造函数注入,其他成员通过 setter 注入。我似乎无法让 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);
....
}

有什么想法吗?

谢谢!

【问题讨论】:

    标签: spring constructor code-injection mockito setter


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 谢谢!那么如何注入我的 setter 注入模拟?
    • 我建议您在测试对象上显式调用设置器。不要依赖@InjectMocks 为你做这件事。
    • @Yotam 你必须自己写。在设计良好的对象中,不要在使用构造函数创建对象之后继续使用setter初始化对象,这很糟糕,通常你应该始终避免可变性,因为它可以破坏您的对象内部、容器和其他协作者。
    • 它选择的那个永远是参数最多的那个。 点赞,谢谢。解决了我的问题。我们无法选择使用哪个构造函数这一事实真的很糟糕……但我不是模拟专家。不幸的是。
    • @Beezer 您可以向 Mockito 团队提出功能请求。我同意如果有某种方法可以指定InjectMocks 使用哪个构造函数会很好(而且我不认为自从我写了大约八年以来他们已经添加了它这个答案,但我不确定)。
    【解决方案2】:

    虽然强烈建议使用构造函数注入,并且我强烈建议不要混合注入方法,但我遇到过这样一个我无法重构的类。为了解决这个问题,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()));
    }
    

    【讨论】:

      【解决方案3】:

      这是 mockito 框架想要的。看到这个Github Issue

      我发现如果您从任何其他类扩展您的测试类(我创建了一个具有有意义名称和小注释的空类),则可以绕过此行为。

      我不知道具体原因,但我相信在扩展测试类的情况下,任何 mockito 内部例程都会进行两次注入。可能是因为 mockito 遍历类 hiracy 并且有一个内部例程,该例程在第一次和第二次注入构造函数时,他进行了 setter/property 注入。只是一个猜测。如果我有时间,我会看看 mockito 的实现。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-25
          • 1970-01-01
          相关资源
          最近更新 更多