JUnit 5 Spring Security WebMvcTest без бина типа PasswordEncoder

#java #spring #spring-boot #junit5

Вопрос:

Я пытаюсь протестировать базовый контроллер страниц, который возвращает шаблон thymeleaf. Контроллер выглядит следующим образом:

 @Controller
public class EntryOverviewController {

    @GetMapping(ApplicationConstants.URL_ENTRY_OVERVIEW)
    public String getPage(Model model) {

        return ApplicationConstants.VIEW_ENTRY_OVERVIEW;
    }
 

мой WebSecurityConfig выглядит так:

 @EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Slf4j
@Configuration
@Order(1005)
public class WebSecurityConfig {

    @Configuration
    @Order(1005)
    public class WebAppSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http.authorizeRequests().antMatchers("/**").permitAll().and().headers().defaultsDisabled().cacheControl().and()
                    .httpStrictTransportSecurity().includeSubDomains(false).maxAgeInSeconds(31536000).and().frameOptions().disable().and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
        }
    }

    @Order(1004)
    @Configuration
    public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private PasswordEncoder passwordEncoder;

        @Value("${management.endpoints.web.access.user}")
        private String user;

        @Value("${management.endpoints.web.access.password}")
        private String password;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.to(HealthEndpoint.class))
                    .permitAll().anyRequest().authenticated().and().httpBasic().and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser(user).password(passwordEncoder.encode(password)).roles();
        }
    }

    /** Password encoder */
    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
 

Мой тест выглядит так:

 @ExtendWith(SpringExtension.class)
@WebMvcTest(EntryOverviewController.class)
class EntryOverviewControllerTest {

    @Autowired
    private MockMvc mvc;

    @WithMockUser(value = "user")
    @Test
    public void testCorrectModel() throws Exception {

      mvc.perform(get(ApplicationConstants.URL_ENTRY_OVERVIEW)).andExpect(status().isOk()).andExpect(model().attributeExists("registerEntry"))
                .andExpect(view().name(ApplicationConstants.VIEW_ENTRY_OVERVIEW));
    }

}
 

Теперь, когда я хочу выполнить свой тест junit 5, он завершается ошибкой с сообщением об ошибке org.springframework.beans.factory.Исключение NoSuchBeanDefinitionException: Нет квалифицирующего компонента типа ‘org.springframework.security.crypto.пароль.Доступен кодер пароля: ожидается, что по крайней мере 1 компонент, который квалифицируется как кандидат на автоматическое подключение. Аннотации зависимостей: {@org.springframework.beans.factory.аннотация.Автоматическая проводка(требуется=верно)}

Когда я издеваюсь над кодером пароля с помощью @MockBean, он выдает ошибку, пароль не может быть нулевым.

Ответ №1:

Вместо автоматического подключения кодера пароля просто используйте кодер пароля(). См. Раздел Зависимости между компонентами здесь https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/beans.html#beans-java-configuration-annotation.

Ответ №2:

Есть ли причина, по которой вы хотите назвать компонент кодировщика? У вас нет нескольких реализаций PasswordEncoder.

 @Bean(name = "passwordEncoder")
  public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
  }
 

Я думаю, что у весны есть проблема, когда она издевается над этой реализацией. Скорее, вы могли бы просто

 @Bean
  public PasswordEncoder passwordEncoder() {
   return new BCryptPasswordEncoder();
  }
 

и это должно сработать.

Ответ №3:

Я решил эту проблему простым изменением аннотации класса.

 @WebMvcTest(EntryOverviewController.class)
@Import(WebSecurityConfig.class)
class EntryOverviewControllerTest {
...
}
 

поэтому я добавил оператор @Import, и это сработало