How can I Integrate SpringSecuirty to My SpringBootTest?

112 Views Asked by At

I'm trying to test a comment_post method. Comment has many - to - one relationship with User Entity which comes from Spring Security. I connected this relationship by using Principal. I think I made it working properly, but having trouble applying it to test.

Problem is that Comment Posting method gets user by finding User in Repository using Principal's email attribute, So I need to apply SecurityContext to test, but I have no idea how to apply this function to test.

By Searching, I found out that I can make SpringSecurityContext by @WithSecurityContext annotation, so I'm trying to apply it but having this error

java.lang.RuntimeException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springboot.web.CommentsApiControllerTest$WithUserDetailsSecurityContextFactory': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'springboot.web.CommentsApiControllerTest' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

I'm not even sure that my approach is correct. tbh, I kind of feel lost, maybe it's because I'm new to SpringBoot, also Security.

Here's my codes.

CommentService

@RequiredArgsConstructor
@Service
public class CommentService {

private final CommentRepository commentRepository;
private final PostsRepository postsRepository;
private final UserDetailService userDetailService;

@Transactional
public Long commentSave(CommentSaveRequestDto requestDto, Long id) {
    Posts post = postsRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다"));
    requestDto.setPosts(post);

    User user = userDetailService.returnUser();
    requestDto.setUser(user);

    return commentRepository.save(requestDto.toEntity()).getId();
}

`

UserDetailService

@RequiredArgsConstructor
@Service
public class UserDetailService {

private final UserRepository userRepository;

public User returnUser() {
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String userName;

    if (principal instanceof UserDetails) {
        userName = ((UserDetails) principal).getUsername();
    } else {
        userName = principal.toString();
    }

    int start = userName.indexOf("email")+6;
    int end = userName.indexOf(".com,")+4;
    String email = userName.substring(start, end);

    User user = userRepository.findByEmail(email).orElse(null);

    return user;
}

CommentSaveRequestDto

@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class CommentSaveRequestDto {
    private String comment;
    private Posts posts;

    private User user;

    /* Dto -> Entity */
    public Comment toEntity() {
        return Comment.builder()
                .comment(comment)
                .posts(posts)
                .user(user)
                .build();
    }
}

And here is my CommentsApiControllrTest

@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)
    public @interface WithMockCustomUser {
        String name() default "testName";

        String email() default "[email protected]";

        Role role() default Role.USER;
    }

    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;
        }
    }

    @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());

    }

All I want is to make a mock Security User in test, so that

User user = userDetailService.returnUser();

this line in CommentService don't make any error.

Just a little tip would be really helpful to me. Thank you in advance.

0

There are 0 best solutions below