Я использую Spring Boot 2.0.8.RELEASE. У меня есть контроллер, который имеет следующую конструкцию метода
@PostMapping(value = "/profile/change-password", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Mono<String> changePasswordSubmit(Authentication authentication, @RequestBody MultiValueMap<String, String> formData) {
И мой модульный тест, который выглядит так:
@RunWith(SpringRunner.class)
@WebFluxTest(controllers = ChangePasswordController.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Import({ThymeleafAutoConfiguration.class, SpringSecurityContextUtils.class})
@WithMockUser(username = "test", password = "password")
public class ChangePasswordControllerTest {
@Autowired
WebTestClient webTestClient;
@MockBean
SpringUserDetailsRepository userDetailsRepository;
@Autowired
ChangePasswordController controller;
@MockBean
Authentication authentication;
@Test
public void addNewEntrySubmit() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.put("password1", Collections.singletonList("password"));
formData.put("password2", Collections.singletonList("password"));
webTestClient.post().uri("/profile/change-password").contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/page/1");
// verify(userDetailsRepository).updatePassword(captor.capture(), captor.capture());
doNothing().when(userDetailsRepository).updatePassword(any(), any());
}
}
Моя проблема в том, что когда я запускаю тест, значение аутентификации на контроллере равно нулю. Я попытался добавить контекст безопасности, но у меня возникли проблемы с его правильностью. Как это исправить
Обновлять: Ссылка на пример репозитория: https://github.com/dmbeer/thymeleaf-spring-security-test
Привет @jzheaux, спасибо за это, кажется, если я изменю вашу версию Spring Boot на 2.0.8.RELEASE так же, как и моя, произойдет сбой с той же ошибкой. Я обновил вопрос с версией весенней загрузки.




До Spring Boot 5.1.x вам необходимо вручную добавить конфигурацию фильтра Spring Security:
WebTestClient webTestClient = WebTestClient
.bindToController(new ChangedPasswordController())
.webFilter(new SecurityContextServerWebExchangeWebFilter())
.apply(springSecurity())
.configureClient()
.build();
В версии 5.1.x @WebFluxTest добавляет эти вызовы автоматически, поэтому вам не нужно этого делать.
Вы можете увидеть пример этого в репозиторий Spring Security.
Привет @jzheaux, спасибо за это. Однако, попробовав как этот подход, так и тот, что указан в документации. У меня все еще столько проблем, что теперь оба теста не отправляются с той же ошибкой, что и раньше, а getthepage не может найти представление. Я обновил вопрос со ссылкой на проект, который воспроизводит проблему, спасибо
Я скачал ваш код, и, к счастью, Authentication разрешился правильно. Если вы отлаживаете метод контроллера, вы увидите, что экземпляр Authentication действительно существует. Причина NPE, выдаваемого тестом, заключается в том, что в userDetailsRepository нет имитируемого поведения.
Привет После многих проб и ошибок у меня все работает. Пришлось немного изменить его на bindToApplicationContext, чтобы он мог найти другие страницы. Окончательное решение ниже.
Потрясающий! Рад слышать это.
Итак, после помощи @jzheaux и соответствующей документации, а также руководства для webflux https://docs.spring.io/spring-security/site/docs/5.0.11.RELEASE/reference/html/test-webflux.html
Мой модульный тест выглядит следующим образом:
@RunWith(SpringRunner.class)
@Import({ThymeleafAutoConfiguration.class})
@WebFluxTest(controllers = ChangePasswordController.class)
@WithMockUser(username = "test", authorities = {"ROLE_ADMIN"})
@ContextConfiguration
public class ChangePasswordControllerTest {
@Autowired
ApplicationContext context;
private WebTestClient webTestClient;
@MockBean
SpringUserDetailsRepository userDetailsRepository;
@Captor
private ArgumentCaptor<String> captor;
@Before
public void setUp() throws Exception {
webTestClient = WebTestClient.bindToApplicationContext(context)
.webFilter(new SecurityContextServerWebExchangeWebFilter())
.apply(springSecurity())
.configureClient()
.build();
}
@Test
public void getChangePasswordPageTest() {
EntityExchangeResult<String> result = webTestClient
.mutateWith(csrf())
.get().uri("/profile/change-password")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).returnResult();
assertThat(result.getResponseBody(), stringContainsInOrder(Arrays.asList("<title>Change Password</title>",
"<input type=\"password\" class=\"form-control\" id=\"password1\" name=\"password1\">")));
}
@Test
public void addNewEntrySubmit() {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.put("password1", Collections.singletonList("password"));
formData.put("password2", Collections.singletonList("password"));
given(userDetailsRepository.updatePassword(any(), any())).willReturn(Mono.empty());
webTestClient.mutateWith(csrf()).post().uri("/profile/change-password").contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData)).exchange().expectStatus().isSeeOther().expectHeader().valueEquals(HttpHeaders.LOCATION, "/page/1");
verify(userDetailsRepository).updatePassword(captor.capture(), captor.capture());
// doNothing().when(userDetailsRepository).updatePassword(any(), any());
}
}```
Кажется, что-то еще может происходить. Когда я копирую ваш тест и контроллер в свое приложение, тест завершается нормально. Мне пришлось настроить его, чтобы удалить некоторые из ваших внутренних компонентов и добавить токен csrf, но вы можете увидеть мой пример приложения по адресу github.com/jzheaux/so-55365324.