问题描述
我在尝试测试接收UserDetails
作为用@AuthenticationPrincipal.
I am having trouble trying to test a REST endpoint that receives an UserDetails
as a parameter annotated with @AuthenticationPrincipal.
似乎没有使用在测试场景中创建的用户实例,而是尝试使用默认构造函数实例化:org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.andrucz.app.AppUserDetails]: No default constructor found;
It seems like the user instance created in the test scenario is not being used, but an attempt to instantiate using default constructor is made instead: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.andrucz.app.AppUserDetails]: No default constructor found;
REST端点:
@RestController
@RequestMapping("/api/items")
class ItemEndpoint {
@Autowired
private ItemService itemService;
@RequestMapping(path = "/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Callable<ItemDto> getItemById(@PathVariable("id") String id, @AuthenticationPrincipal AppUserDetails userDetails) {
return () -> {
Item item = itemService.getItemById(id).orElseThrow(() -> new ResourceNotFoundException(id));
...
};
}
}
测试类:
public class ItemEndpointTests {
@InjectMocks
private ItemEndpoint itemEndpoint;
@Mock
private ItemService itemService;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(itemEndpoint)
.build();
}
@Test
public void findItem() throws Exception {
when(itemService.getItemById("1")).thenReturn(Optional.of(new Item()));
mockMvc.perform(get("/api/items/1").with(user(new AppUserDetails(new User()))))
.andExpect(status().isOk());
}
}
如何在不切换到webAppContextSetup
的情况下解决该问题?我想编写可以完全控制服务模拟的测试,所以我正在使用standaloneSetup.
How can I solve that problem without having to switch to webAppContextSetup
? I want to write tests having total control of service mocks, so I am using standaloneSetup.
推荐答案
这可以通过将HandlerMethodArgumentResolver
注入到Mock MVC上下文或独立设置中来完成.假设您的@AuthenticationPrincipal
类型为ParticipantDetails
:
This can be done by injection a HandlerMethodArgumentResolver
into your Mock MVC context or standalone setup. Assuming your @AuthenticationPrincipal
is of type ParticipantDetails
:
private HandlerMethodArgumentResolver putAuthenticationPrincipal = new HandlerMethodArgumentResolver() {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(ParticipantDetails.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return new ParticipantDetails(…);
}
};
此参数解析程序可以处理类型ParticipantDetails
,只是凭空创建它,但是您会看到很多上下文.稍后,此参数解析器将附加到模拟MVC对象:
This argument resolver can handle the type ParticipantDetails
and just creates it out of thin air, but you see you get a lot of context. Later on, this argument resolver is attached to the mock MVC object:
@BeforeMethod
public void beforeMethod() {
mockMvc = MockMvcBuilders
.standaloneSetup(…)
.setCustomArgumentResolvers(putAuthenticationPrincipal)
.build();
}
这将导致您的@AuthenticationPrincipal
带注释的方法参数填充有来自解析器的详细信息.
This will result in your @AuthenticationPrincipal
annotated method arguments to be populated with the details from your resolver.
这篇关于在单元测试Spring REST控制器时注入@AuthenticationPrincipal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!