Котлин, Весенняя книга, Mockito, @InjectMocks, Используя другие насмешки, чем созданные

#spring-boot #kotlin #mockito

#пружинный ботинок #котлин #мокито

Вопрос:

Я пытаюсь протестировать такой класс, как

 @TestInstance(TestInstance.Lifecycle.PER_CLASS) @SpringBootTest(classes = [TestConfig::class]) @ExtendWith(MockitoExtension::class) class TotalCalculatorTests {  @Mock lateinit var jdbcRepo: JDBCRepo  @Mock lateinit var userCache : LoadingCachelt;ApproverLevel, Listlt;Approvergt;gt;  @InjectMocks lateinit var calculator: TotalCalculator  @Test fun totalOrder() {  // val calculator = TotalCalculator(userCache,jdbcRepo)  calculator.total(ItemA(),ItemB())  verify(jdbcRepo).getTotal()  } }  

Я получаю ошибку Actually, there were zero interactions with this mock. , но если я раскомментирую // val calculator = TotalCalculator(userCache,jdbcRepo) , это сработает. Я предполагал, что mockito будет использовать насмешки из тестового класса, но это, похоже, не так. Как я могу получить экземпляр JDBCRepo, используемый @InjectMocks?

Ответ №1:

Похоже, вы хотели бы запустить полноценный тест весенней загрузки со всеми компонентами, но в контексте приложения вы хотели бы «поиздеваться» над некоторыми реальными компонентами и предоставить свою собственную (макет-у) реализацию.

Если это так, то использование @Mock здесь неверно. @Mock не имеет никакого отношения к весне, это чисто шутливая вещь. Он действительно может создать макет для вас, но он не «заменит» реальную реализацию этой макетной реализацией в контексте приложения spring boot.

Для этой цели @MockBean вместо этого используйте аннотацию. Это что-то из весенней «вселенной», которая действительно создает макет, управляемый макетом, под капотом, но заменяет обычный компонент в контексте приложения (или даже просто добавляет эту макет-реализацию в контекст приложения, если реальный компонент даже не существует).

Еще одна вещь, которую следует учитывать, — как вы получаете TotalCalculator боб (хотя вы прямо не задаете этого в вопросе).

Сам TotalCalculator по себе, вероятно, является пружинным компонентом, который создает для вас spring boot, поэтому, если вы хотите запустить «полноценный» тест, вам следует взять экземпляр этого компонента из контекста приложения, а не создавать экземпляр самостоятельно. Используйте аннотации @Autowired для этой цели:

 @TestInstance(TestInstance.Lifecycle.PER_CLASS) @SpringBootTest(classes = [TestConfig::class]) @ExtendWith(MockitoExtension::class) // you don't need this class TotalCalculatorTests {  @MockBean lateinit var jdbcRepo: JDBCRepo  @MockBean lateinit var userCache : LoadingCachelt;ApproverLevel, Listlt;Approvergt;gt;  @Autowired // injected by the spring test infrastructure lateinit var calculator: TotalCalculator  @Test fun totalOrder() {  // val calculator = TotalCalculator(userCache,jdbcRepo)  calculator.total(ItemA(),ItemB())  verify(jdbcRepo).getTotal()  } }   

Теперь все это правильно, если ваша цель действительно запустить полный микросервис spring boot и провести «интеграционное» тестирование

Если в качестве альтернативы вы просто хотите проверить код своего калькулятора, это может быть излишним, вы можете в конечном итоге использовать mockito и обычное модульное тестирование. В этом случае, однако, вам не нужно даже запускать spring (читай «создать контекст приложения») во время теста, и, конечно, нет необходимости использовать @SpringBootTest / @MockBean / @Autowired .

Так что это в значительной степени зависит от того, какой тест здесь действительно требуется

Комментарии:

1. да, я хочу интеграционный тест, большое вам спасибо за удивительный ответ!!!