如何在单元测试期间为Thymeleaf模板传递身份验证

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在单元测试期间为Thymeleaf模板传递身份验证相关的知识,希望对你有一定的参考价值。

我正在使用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());
    }
}

我的问题是,当我运行测试时,控制器上的Authentication值为null。我试图添加安全上下文,但遇到了正确的问题。我该如何解决

更新:链接到示例存储库:https://github.com/dmbeer/thymeleaf-spring-security-test

答案

在Spring Boot 5.1.x之前,您需要手动添加Spring Security过滤器配置:

WebTestClient webTestClient = WebTestClient
        .bindToController(new ChangedPasswordController())
        .webFilter(new SecurityContextServerWebExchangeWebFilter())
        .apply(springSecurity())
        .configureClient()
        .build();

在5.1.x中,@WebFluxTest会自动添加这些调用,因此您不必这样做。

你可以在the Spring Security repo中看到它的一个例子。

另一答案

所以在@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());
    }
}```

以上是关于如何在单元测试期间为Thymeleaf模板传递身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何对使用 thymeleaf 的安全控制器进行单元测试(不获取 TemplateProcessingException)?

如何在单元测试期间访问 .war 类路径中的文件?

模板文件“Error/error500.ctp”丢失。在 CakePHP 的单元测试期间

如何在单元测试期间测试所需的 argparse 参数?

如何使用thymeleaf显示控制传递过来的数据

在spring boot 项目中使用thymeleaf模板