[트러블 슈팅] 16. V3 - 테스트 실행할 때 왜 오류가 떴는가

귤's avatar
May 29, 2025
[트러블 슈팅] 16. V3 - 테스트 실행할 때 왜 오류가 떴는가

문제 : Run Tests in “blog.test” 했을 때, 계속 오류가 떴음

notion image

해결 방안

1. master branch로 오면서 .propertiesdev로 안 고쳤음

spring.profiles.active=dev
notion image

2. LogFilter에 userAgentnull일 경우 빈 문자열 ""로 대체해주는 코드 입력 안 했음 (왜? 위치 몰라서, 잘 기억해두기)

package shop.mtcoding.blog._core.filter; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import java.io.IOException; @Slf4j public class LogFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; String uri = req.getRequestURI(); String ip = req.getRemoteAddr(); String userAgent = req.getHeader("User-Agent"); userAgent = userAgent == null ? "" : userAgent; String msg = "[로그] ${uri} | IP: ${ip} | UA: ${ua}" .replace("${uri}", uri) .replace("${ip}", ip) .replace("${ua}", userAgent); log.info(msg); filterChain.doFilter(servletRequest, servletResponse); } }
LogFilter
notion image

3. 어쩌다가 join_username_uk_fail_test 부분이 중복이었는데, 중복인 줄 모르고 오류 라고만 생각해서 삭제를 안 했었음

중복이었던 코드
@Test public void join_username_uk_fail_test() throws Exception { // 이 메서드를 호출한 주체에게 예외 위임 -> 지금은 jvm 이다 // given -> 가짜 데이터 UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO(); reqDTO.setEmail("ssar@nate.com"); reqDTO.setPassword("1234"); reqDTO.setUsername("ssar"); String requestBody = om.writeValueAsString(reqDTO); // System.out.println(requestBody); // {"username":"haha","password":"1234","email":"haha@nate.com"} // when -> 테스트 실행 ResultActions actions = mvc.perform( // 주소가 틀리면 터지고, json 아닌거 넣으면 터지고, 타입이 달라도 터지고. 따라서 미리 터진다고 알려줌 MockMvcRequestBuilders .post("/join") .content(requestBody) .contentType(MediaType.APPLICATION_JSON) ); // eye -> 결과 눈으로 검증 String responseBody = actions.andReturn().getResponse().getContentAsString(); //System.out.println(responseBody); // {"status":200,"msg":"성공","body":{"id":4,"username":"haha","email":"haha@nate.com","createdAt":"2025-05-13 11:45:23.604577"}} // then -> 결과를 코드로 검증 // json의 최상위 객체를 $ 표기한다 actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(400)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("중복된 유저네임이 존재합니다")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body").value(Matchers.nullValue())); }
수정한 코드
package shop.mtcoding.blog.integre; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.transaction.Transactional; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import shop.mtcoding.blog.MyRestDoc; import shop.mtcoding.blog._core.util.JwtUtil; import shop.mtcoding.blog.user.User; import shop.mtcoding.blog.user.UserRequest; import static org.hamcrest.Matchers.matchesPattern; // 5. (컨트롤러 가기) 문서 만들기 (상속하고, mvc 부모 옮기고, 부모에 갔으니 삭제하고, andDO 설정하고) @Transactional @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) // MOCK -> 가짜 환경을 만들어 필요한 의존관계를 다 메모리에 올려서 테스트 public class UserControllerTest extends MyRestDoc { @Autowired private ObjectMapper om; // json <-> java Object 변환 해주는 객체. IoC에 objectMapper가 이미 떠있음 private String accessToken; @BeforeEach public void setUp() { // 테스트 시작 전에 실행할 코드 System.out.println("setUp"); User ssar = User.builder() .id(1) .username("ssar") .build(); accessToken = JwtUtil.create(ssar); } @AfterEach public void tearDown() { // 끝나고 나서 마무리 함수 // 테스트 후 정리할 코드 System.out.println("tearDown"); } @Test public void join_username_uk_fail_test() throws Exception { // 이 메서드를 호출한 주체에게 예외 위임 -> 지금은 jvm 이다 // given -> 가짜 데이터 UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO(); reqDTO.setEmail("ssar@nate.com"); reqDTO.setPassword("1234"); reqDTO.setUsername("ssar"); String requestBody = om.writeValueAsString(reqDTO); // System.out.println(requestBody); // {"username":"haha","password":"1234","email":"haha@nate.com"} // when -> 테스트 실행 ResultActions actions = mvc.perform( // 주소가 틀리면 터지고, json 아닌거 넣으면 터지고, 타입이 달라도 터지고. 따라서 미리 터진다고 알려줌 MockMvcRequestBuilders .post("/join") .content(requestBody) .contentType(MediaType.APPLICATION_JSON) ); // eye -> 결과 눈으로 검증 String responseBody = actions.andReturn().getResponse().getContentAsString(); //System.out.println(responseBody); // {"status":200,"msg":"성공","body":{"id":4,"username":"haha","email":"haha@nate.com","createdAt":"2025-05-13 11:45:23.604577"}} // then -> 결과를 코드로 검증 // json의 최상위 객체를 $ 표기한다 actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(400)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("중복된 유저네임이 존재합니다")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body").value(Matchers.nullValue())); actions.andDo(MockMvcResultHandlers.print()).andDo(document); } @Test public void join_test() throws Exception { // 이 메서드를 호출한 주체에게 예외 위임 -> 지금은 jvm 이다 // given -> 가짜 데이터 UserRequest.JoinDTO reqDTO = new UserRequest.JoinDTO(); reqDTO.setEmail("haha@nate.com"); reqDTO.setPassword("1234"); reqDTO.setUsername("haha"); String requestBody = om.writeValueAsString(reqDTO); // System.out.println(requestBody); // {"username":"haha","password":"1234","email":"haha@nate.com"} // when -> 테스트 실행 ResultActions actions = mvc.perform( // 주소가 틀리면 터지고, json 아닌거 넣으면 터지고, 타입이 달라도 터지고. 따라서 미리 터진다고 알려줌 MockMvcRequestBuilders .post("/join") .content(requestBody) .contentType(MediaType.APPLICATION_JSON) ); // eye -> 결과 눈으로 검증 String responseBody = actions.andReturn().getResponse().getContentAsString(); //System.out.println(responseBody); // {"status":200,"msg":"성공","body":{"id":4,"username":"haha","email":"haha@nate.com","createdAt":"2025-05-13 11:45:23.604577"}} // then -> 결과를 코드로 검증 // json의 최상위 객체를 $ 표기한다 actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(200)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("성공")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.id").value(4)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.username").value("haha")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.email").value("haha@nate.com")); actions.andDo(MockMvcResultHandlers.print()).andDo(document); } @Test public void login_test() throws Exception { // given UserRequest.LoginDTO reqDTO = new UserRequest.LoginDTO(); reqDTO.setUsername("ssar"); reqDTO.setPassword("1234"); String requestBody = om.writeValueAsString(reqDTO); // System.out.println(requestBody); // when ResultActions actions = mvc.perform( MockMvcRequestBuilders .post("/login") .content(requestBody) .contentType(MediaType.APPLICATION_JSON) ); // eye String responseBody = actions.andReturn().getResponse().getContentAsString(); System.out.println(responseBody); // then (jwt 길이만 검증) -> 길이 변환 가능. 패턴만 확인 actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(200)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("성공")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.accessToken", matchesPattern("^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"))); actions.andDo(MockMvcResultHandlers.print()).andDo(document); } @Test public void update_test() throws Exception { // given UserRequest.UpdateDTO reqDTO = new UserRequest.UpdateDTO(); reqDTO.setEmail("ssar@gmail.com"); reqDTO.setPassword("1234"); String requestBody = om.writeValueAsString(reqDTO); // System.out.println(requestBody); // when ResultActions actions = mvc.perform( MockMvcRequestBuilders .put("/s/api/user") .content(requestBody) .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer " + accessToken) ); System.out.println("AccessToken : " + accessToken); // eye String responseBody = actions.andReturn().getResponse().getContentAsString(); System.out.println(responseBody); // then actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(200)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("성공")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.id").value(1)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.username").value("ssar")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.email").value("ssar@gmail.com")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.createdAt", matchesPattern("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+"))); actions.andDo(MockMvcResultHandlers.print()).andDo(document); } @Test public void check_username_available_test() throws Exception { // given String username = "ssar"; // when ResultActions actions = mvc.perform( MockMvcRequestBuilders .get("/api/check-username-available/{username}", username) ); // eye String responseBody = actions.andReturn().getResponse().getContentAsString(); System.out.println(responseBody); // then actions.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(200)); actions.andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("성공")); actions.andExpect(MockMvcResultMatchers.jsonPath("$.body.available").value(false)); actions.andDo(MockMvcResultHandlers.print()).andDo(document); } }

4. 컨트롤러들에 mvc 부모 옮기고 옮겨졌으니까 삭제해야 하는데 선생님 깃 복붙 한다고 삭제했던 거 다시 되돌아 옴 (한번 더 확인하기)

@Transactional @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
notion image

5. 버그 : 빌드 한번 더 해야지 resource 폴더 안에 api.html이 생성됨

notion image
notion image

결과

notion image
notion image
notion image
notion image
 
Share article

gyul