Issue
I'm trying to make custom SecurityContext and using it by @WithMockCustomUser(custom annotation) to test my Comments_post feature. The reason I'm using @WithMockCustomeUser is that I need user's email to verify user.
I refered to
href="https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/test-method.html" rel="nofollow noreferrer">https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/test-method.html to use it.
But somehow I'm getting above error.
I think there's some misunderstanding combining security and test.
Here's my test Code.
final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithUserDetails> {
private final UserDetailsService userDetailsService;
@Autowired
public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public org.springframework.security.core.context.SecurityContext createSecurityContext(WithUserDetails withUser) {
String username = withUser.value();
Assert.hasLength(username, "value() must be non-empty String");
UserDetails principal = userDetailsService.loadUserByUsername(username);
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Transactional
public class CommentsApiControllerTest {
@LocalServerPort
private int port;
@Autowired
private PostsRepository postsRepository;
@Autowired
private CommentRepository commentRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private PostsService postsService;
@Autowired
private CommentService commentService;
@Autowired
private UserDetailService userDetailsService;
@Autowired
private WebApplicationContext context;
@Autowired ObjectMapper objectMapper;
private MockMvc mvc;
@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.apply(sharedHttpSession())
.build();
}
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class, setupBefore = TestExecutionEvent.TEST_EXECUTION)
public @interface WithMockCustomUser {
String name() default "testName";
String email() default "[email protected]";
Role role() default Role.USER;
}
@After
public void tearDown() throws Exception {
postsRepository.deleteAll();
commentRepository.deleteAll();
}
@Test
@WithMockCustomUser
@Transactional // 프록시 객체에 실제 데이터를 불러올 수 있게 영속성 컨텍스트에서 관리
public void comment_등록() throws Exception {
// given
String title = "title";
String content = "content";
User user = userRepository.save(User.builder()
.name("name")
.email("[email protected]")
.picture("fakePic.com")
.role(Role.USER)
.build());
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.user(user)
.build();
postsRepository.save(requestDto.toEntity());
String comment = "comment";
Posts posts = postsRepository.findAll().get(0);
CommentSaveRequestDto saveRequestDto = CommentSaveRequestDto.builder()
.comment(comment)
.posts(posts)
.build();
Long id = posts.getId();
String url = "http://localhost:"+ port + "/api/posts/" + id + "/comments";
//when
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(saveRequestDto)))
.andExpect(status().isOk())
.andDo(print());
}
And it is full error trace
java.lang.IllegalStateException: Unable to create SecurityContext using @springboot.web.CommentsApiControllerTest$WithMockCustomUser(name=testName, [email protected], role=USER)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:126)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:96)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.beforeTestMethod(WithSecurityContextTestExecutionListener.java:62)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:291)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to org.springframework.security.test.context.support.WithUserDetails
at springboot.web.WithUserDetailsSecurityContextFactory.createSecurityContext(CommentsApiControllerTest.java:64)
at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:123)
... 26 more
Thanks in advance..!
Solution
Take a look at the stacktrace, the actual error is:
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to org.springframework.security.test.context.support.WithUserDetails
at springboot.web.WithUserDetailsSecurityContextFactory.createSecurityContext(CommentsApiControllerTest.java:64)
You are not using @WithUserDetails
in your tests, you are using @WithMockCustomUser
, therefore I think your class declaration should be:
final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
// ...
}
Answered By - Marcus Hert da Coregio
Answer Checked By - Robin (JavaFixing Admin)