YeahyunKim / SpartaNbCamp-Javafeed-TestCode-pp

팀프로젝트였던 Javafeed 프로젝트에 Test code를 추가하여 학습하는 레포지토리입니다.
0 stars 0 forks source link

[필수 3] - Controller Test 추가하기 #3

Open YeahyunKim opened 2 weeks ago

YeahyunKim commented 2 weeks ago
  1. MockSecurityFilter 생성
    
    package com.sparta.javafeed.mvc;

import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder;

import java.io.IOException;

//Security가 동작하면 테스트를 하기 매우 까다롭다. 따라서 우리는 가짜 SecurityFilter를 생성하여 이 필터에서 Security를 통한 테스트를 진행할 수 있다. public class MockSpringSecurityFilter implements Filter { @Override public void init(FilterConfig filterConfig) {}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    //SecurityContextHolder = 인증 객체를 담고있는 컨텍스트를 담는 공간, 따라서 SecurityContext에 점근하기 위해서는 SecurityContextHolder 가 반드시 필요하다.
    SecurityContextHolder.getContext()
            .setAuthentication((Authentication) ((HttpServletRequest) req).getUserPrincipal());//SecurityContext 안에 가짜 인증 코드를 넣어줌
    chain.doFilter(req, res);
}

@Override
public void destroy() {
    SecurityContextHolder.clearContext();
}

}


<br>

2. `UserControllerTest` 코드 추가
```java
package com.sparta.javafeed.mvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.javafeed.config.SecurityConfig;
import com.sparta.javafeed.controller.UserController;
import com.sparta.javafeed.dto.PasswordReqeustDto;
import com.sparta.javafeed.dto.PasswordUpdateDto;
import com.sparta.javafeed.dto.SignupRequestDto;
import com.sparta.javafeed.dto.UserInfoRequestDto;
import com.sparta.javafeed.entity.User;
import com.sparta.javafeed.jwt.JwtUtil;
import com.sparta.javafeed.security.UserDetailsImpl;
import com.sparta.javafeed.service.UserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;

import java.security.Principal;

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

@WebMvcTest(value = UserController.class,
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = SecurityConfig.class
                )
        })
public class UserControllerTest {
    private MockMvc mockMvc; // 컨트롤러 테스트를 위해 MockMvc 객체 생성 // 가짜 http요청을 보내기 위해 생성

    private Principal mockPrincipal; //가짜 인증을 위해 가짜 Principal 생성 //Principal은 현재 로그인된 사용자

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    UserService userService;

    @MockBean
    JwtUtil jwtUtil;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(new MockSpringSecurityFilter()))
                .build();
    }

    private void mockUserSetup() {
        SignupRequestDto requestDto = SignupRequestDto.builder()
                .accountId("insidesy123")
                .password("Insidesyt123!@#")
                .email("insidesy123@gmail.com")
                .name("김예현")
                .build();

        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode(requestDto.getPassword());

        User user = new User(requestDto, encodedPassword);
        UserDetailsImpl userDetails = new UserDetailsImpl(user);
        mockPrincipal = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    @Test
    @DisplayName("[POST] 회원 가입")
    void signupUser() throws Exception {
        // given
        SignupRequestDto requestDto = SignupRequestDto.builder()
                .accountId("insidesy123")
                .password("Insidesyt123!@#")
                .email("insidesy123@gmail.com")
                .name("김예현")
                .build();

        String requestDtoJson = objectMapper.writeValueAsString(requestDto); //requestDto객체를 JSON형태로 변환

        // when, then
        mockMvc.perform(post("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestDtoJson)
                )
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[PATCH] 회원 탈퇴")
    void deactiveUser() throws Exception {
        // given
        this.mockUserSetup();
        PasswordReqeustDto requestDto = new PasswordReqeustDto("Insidesyt123!@#");
        String requestDtoJson = objectMapper.writeValueAsString(requestDto);

        // when, then
        mockMvc.perform(patch("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestDtoJson)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isAccepted())
                .andDo(print());
    }

    @Test
    @DisplayName("[POST] 로그아웃")
    void logout() throws Exception {
        // given
        this.mockUserSetup();

        // when, then
        mockMvc.perform(post("/users/logout")
                        .contentType(MediaType.APPLICATION_JSON)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[GET] 유저조회")
    void getUser() throws Exception {
        // given
        this.mockUserSetup();

        // when, then
        mockMvc.perform(get("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[PUT] 유저 업데이트")
    void updateUser() throws Exception {
        // given
        this.mockUserSetup();
        UserInfoRequestDto requestDto = UserInfoRequestDto.builder()
                .name("김예현2")
                .email("insidesy9999@gmail.com")
                .intro("자바는 너무 재밌습니다.")
                .build();

        String requestDtoJson = objectMapper.writeValueAsString(requestDto);

        // when, then
        mockMvc.perform(put("/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestDtoJson)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[PATCH] 비밀번호 수정")
    void updatePasswordSuccess() throws Exception {
        // given
        this.mockUserSetup();
        PasswordUpdateDto requestDto = new PasswordUpdateDto("Insidesyt123!@#", "iNSidesyt123!@#!@#");
        String passwordInfo = objectMapper.writeValueAsString(requestDto);

        // when, then
        mockMvc.perform(patch("/users/password")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(passwordInfo)
                        .principal(mockPrincipal))
                .andExpect(status().isOk())
                .andDo(print());
    }
}
  1. NewsfeedControllerTest 코드 추가
    
    package com.sparta.javafeed.mvc;

import com.fasterxml.jackson.databind.ObjectMapper; import com.sparta.javafeed.config.SecurityConfig; import com.sparta.javafeed.controller.NewsfeedController; import com.sparta.javafeed.controller.UserController; import com.sparta.javafeed.dto.NewsfeedRequestDto; import com.sparta.javafeed.dto.SignupRequestDto; import com.sparta.javafeed.entity.User; import com.sparta.javafeed.jwt.JwtUtil; import com.sparta.javafeed.security.UserDetailsImpl; import com.sparta.javafeed.service.NewsfeedService; import com.sparta.javafeed.service.UserService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext;

import java.security.Principal;

import static org.junit.jupiter.api.Assertions.*; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(value = NewsfeedController.class, excludeFilters = { @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = SecurityConfig.class ) }) class NewsfeedControllerTest { private MockMvc mockMvc; // 컨트롤러 테스트를 위해 MockMvc 객체 생성 // 가짜 http요청을 보내기 위해 생성

private Principal mockPrincipal; //가짜 인증을 위해 가짜 Principal 생성 //Principal은 현재 로그인된 사용자

@Autowired
private WebApplicationContext context;

@Autowired
private ObjectMapper objectMapper;

@MockBean
NewsfeedService newsfeedService;

@MockBean
JwtUtil jwtUtil;

@BeforeEach
void setUp() {
    mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity(new MockSpringSecurityFilter()))
            .build();
}

private void mockUserSetup() {
    SignupRequestDto requestDto = SignupRequestDto.builder()
            .accountId("insidesy123")
            .password("Insidesyt123!@#")
            .email("insidesy123@gmail.com")
            .name("김예현")
            .build();

    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    String encodedPassword = passwordEncoder.encode(requestDto.getPassword());

    User user = new User(requestDto, encodedPassword);
    UserDetailsImpl userDetails = new UserDetailsImpl(user);
    mockPrincipal = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());

}

@Test
@DisplayName("[POST] 게시물 작성")
void createNewsfeed() throws Exception {
    //given
    this.mockUserSetup();

    NewsfeedRequestDto requestDto = NewsfeedRequestDto.builder()
            .title("자바의 정석")
            .description("자바는 재밌다.")
            .build();

    String requestDtoJson = objectMapper.writeValueAsString(requestDto);

    //when, then
    mockMvc.perform(post("/posts")
                    .contentType(MediaType.APPLICATION_JSON)
                    .principal(mockPrincipal)
                    .content(requestDtoJson)
            )
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("[GET] 게시물 목록 조회")
void updateNewsfeed() throws Exception {
    //given
    String page = "1";
    String searchStartDate = "20240612";
    String searchEndDate = "20240614";

    //when, then
    mockMvc.perform(get("/posts")
                    .param("page", page)
                    .param("searchStartDate", searchStartDate)
                    .param("searchEndDate", searchEndDate)
            )
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("[PUT] 게시물 업데이트")
void getNewsfeed() throws Exception {
    //given
    this.mockUserSetup();
    NewsfeedRequestDto requestDto = NewsfeedRequestDto.builder()
            .title("자바의 정석2")
            .description("자바는 재밌다2")
            .build();

    String requestDtoJson = objectMapper.writeValueAsString(requestDto);

    //when, then
    mockMvc.perform(put("/posts/1")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(requestDtoJson)
                    .principal(mockPrincipal)
            )
            .andExpect(status().isOk())
            .andDo(print());
}

@Test
@DisplayName("[DELETE] 게시물 삭제")
void deleteNewsfeed() throws Exception {
    //given
    this.mockUserSetup();

    //when, then
    mockMvc.perform(delete("/posts/1")
                    .contentType(MediaType.APPLICATION_JSON)
                    .principal(mockPrincipal)
            )
            .andExpect(status().isOk())
            .andDo(print());
}

}


<br>

4. `CommentControllerTest` 코드 추가
```java
package com.sparta.javafeed.mvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.javafeed.config.SecurityConfig;
import com.sparta.javafeed.controller.CommentController;
import com.sparta.javafeed.controller.NewsfeedController;
import com.sparta.javafeed.dto.CommentRequestDto;
import com.sparta.javafeed.dto.NewsfeedRequestDto;
import com.sparta.javafeed.dto.SignupRequestDto;
import com.sparta.javafeed.entity.User;
import com.sparta.javafeed.jwt.JwtUtil;
import com.sparta.javafeed.security.UserDetailsImpl;
import com.sparta.javafeed.service.CommentService;
import com.sparta.javafeed.service.NewsfeedService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.security.Principal;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(value = CommentController.class,
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ASSIGNABLE_TYPE,
                        classes = SecurityConfig.class
                )
        })

class CommentControllerTest {
    private MockMvc mockMvc; // 컨트롤러 테스트를 위해 MockMvc 객체 생성 // 가짜 http요청을 보내기 위해 생성

    private Principal mockPrincipal; //가짜 인증을 위해 가짜 Principal 생성 //Principal은 현재 로그인된 사용자

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private ObjectMapper objectMapper;

    @MockBean
    CommentService commentService;

    @MockBean
    JwtUtil jwtUtil;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity(new MockSpringSecurityFilter()))
                .build();
    }

    private void mockUserSetup() {
        SignupRequestDto requestDto = SignupRequestDto.builder()
                .accountId("insidesy123")
                .password("Insidesyt123!@#")
                .email("insidesy123@gmail.com")
                .name("김예현")
                .build();

        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode(requestDto.getPassword());

        User user = new User(requestDto, encodedPassword);
        UserDetailsImpl userDetails = new UserDetailsImpl(user);
        mockPrincipal = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    @Test
    @DisplayName("[POST] 댓글 작성")
    void addComment() throws Exception{
        //given
        this.mockUserSetup();

        CommentRequestDto requestDto = CommentRequestDto.builder()
                .description("자바를 정말 잘하셔서 부럽습니다")
                .build();

        String requestDtoJson = objectMapper.writeValueAsString(requestDto);

        //when, then
        mockMvc.perform(post("/posts/1/comments")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestDtoJson)
                        .principal(mockPrincipal)
                )
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[GET] 댓글 목록 조회")
    void getComments() throws Exception {
        // when, then
        mockMvc.perform(get("/posts/1/comments"))
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[PUT] 댓글 수정")
    void updateComment() throws Exception {
        // given
        this.mockUserSetup();

        CommentRequestDto requestDto = CommentRequestDto.builder()
                .description("자바를 정말 잘하셔서 부럽습니다2")
                .build();
        String requestDtoJson = objectMapper.writeValueAsString(requestDto);

        // when, then
        mockMvc.perform(put("/posts/1/comments/1")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(requestDtoJson)
                        .principal(mockPrincipal))
                .andExpect(status().isOk())
                .andDo(print());
    }

    @Test
    @DisplayName("[DELETE] 댓글 삭제")
    void deleteComment() throws Exception {
        // given
        this.mockUserSetup();

        // when, then
        mockMvc.perform(delete("/posts/1/comments/1")
                        .principal(mockPrincipal))
                .andExpect(status().isOk())
                .andDo(print());
    }
}