如何解决Spring-Boot WebMvcTest:如何使用身份验证对象参数测试控制器方法?
这是该问题的继续 Spring WebMvcTest how to mock Authentication?
我正在尝试在Spring-boot中测试一个控制器方法,该方法接收一个Authentication
对象作为参数。控制器是带有RestController
注释的@CrossOrigin
。该方法如下所示:
@GetMapping("/authentication")
public String testAuthentication(Authentication authentication) {
UserDetailsStub userDetailsStub = (UserDetailsStub) authentication.getPrincipal();
return userDetailsStub.getUsername();
}
如您所见,我从参数中获取了来自身份验证的主体。
问题是,在我的WebMvcTest
测试案例中,我得到了NullPointerException
,因为在测试案例中,authentication
似乎为空。我的问题是为什么?
我尝试添加一个given
调用,该调用将在测试用例的UserDetails
注释方法中返回一个自定义@PostConstruct
对象,但仍然得到NullPointerException
。
我的测试用例如下:
@Import(SecurityConfiguration.class)
@RunWith(SpringRunner.class)
@WebMvcTest(PDPController.class)
@AutoConfigureMockMvc(addFilters = false)
public class PDPControllerTests {
@Autowired
private MockMvc mvc;
@MockBean(name = "userDetailsService")
private MyUserDetailsService userDetailsService;
//..
@PostConstruct
public void setup() {
given(userDetailsService.loadUserByUsername(anyString()))
.willReturn(new UserDetailsStub());
}
//..
@Test
@WithUserDetails(value = "username",userDetailsServiceBeanName = "userDetailsService")
public void testAuthentication() throws Exception {
mvc.perform(get("/pdps/authentication").secure(true)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
即使在我用authentication
方法提供的情况下,为什么测试用例中的@PostConstruct
为空?
在这里可以找到带有最少代码的GitHub项目,该代码可以再现错误。 https://github.com/Kars1090/SpringSecurityTest
谢谢!
解决方法
克隆您的项目后,我已经实现了在控制器方法中收到有效的Authentication
对象。基本上,您在测试中遇到两个主要问题:
- 不必要的额外配置
- 过滤器的模拟配置错误:
JwtRequestFilter
总的来说,更改如下:
public class UserDetailsStub implements UserDetails {
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;
public UserDetailsStub() {}
public static UserDetailsStub of (User user) {
UserDetailsStub userDetails = new UserDetailsStub();
if (null != user) {
userDetails.username = user.getUsername();
userDetails.password = user.getPassword();
userDetails.authorities = user.getAuthorities();
}
return userDetails;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
// Rest of the code is equal to your version
您的控制器方法:
@GetMapping("/authentication")
public String testAuthentication(Authentication authentication) {
UserDetailsStub userDetailsStub = UserDetailsStub.of((User)
authentication.getPrincipal());
return userDetailsStub.getUsername();
}
测试:
@WebMvcTest(value = PDPController.class)
public class PDPControllerTests {
@Autowired
private MockMvc mvc;
/** You have not to mock the filter because in that case Spring
* won't know how to deal with it,when the list of them
* should be managed.
*
* That is the reason why you had to include
* @AutoConfigureMockMvc(addFilters = false),but that
* is preciselly what was avoiding the creation of your
* Authentication object,because your JwtRequestFilter
* was not being executed.
*
* With the current code,your filter will be executed and
* the Authentication object created.
*/
//@MockBean
//private JwtRequestFilter jwtRequestFilter;
// What you have to mock are the classes the filter uses internally
@MockBean
private MyUserDetailsService userDetailsService;
@MockBean
private JwtService jwtService;
@Test
@WithMockUser
public void test() throws Exception {
mvc.perform(
get("/pdps/authentication").secure(true)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
,
一种(不好的)解决方法是从控制器参数中删除authentication
,而是通过SecurityContextHolder.getContext().getAuthentication()
获得认证。
这将使测试正常进行,而无需进行其他任何更改。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。