Spring MVC(테스팅/슬라이스 테스트/API 계층)

Rina's·2023년 6월 28일

코드스테이츠

목록 보기
54/96

슬라이스 테스트

특정 계층만 잘라서(Slice) 테스트

스모크 테스트(Smoke Test)
애플리케이션의 특정 수정 사항에 영향받는 범위에 한한 제한된 테스트

@SpringBootTest

Spring Boot 기반 테스트를 위한 ApplicationContext생성을 통한 런타임 환경 구성

MockMvc

Spring MVC 테스트 프레임워크
Tomcat같은 서버 없이 Controller 테스트를 가능하게 함

@AutoConfigureMockMvc 를 사용하여 사용 영역을 설정한다
mockMvc.perform() 메서드로 실행하여 ResultActions객체를 리턴한다
ResultActions 객체로 andExpect() 메서드를 사용하여 검증한다

@SpringBootTest vs @WebMvcTest

@SpringBootTest

@AutoConfigureMockMvc와 함께 사용하여
프로젝트에서 사용하는 전체 Bean을 ApplicationContext에 등록하여 사용
편리한 실행 속도가 상대적으로 느리다
DB까지의 통합 테스트에 주로 사용

@WebMvcTest

테스트에 필요한 Bean만 ApplicationContext에 등록하여 사용
Controller에서 의존 객체를 @MockBean로 의존성을 제거한 후 사용
번거로우나 상대적으로 빠르다
Controller 슬라이스 테스트에 주로 사용

Controller(Post) 테스트

기본 설정

  1. @SpringBootTest @AutoConfigureMockMvc 설정
  2. MockMvc클래스 객체 생성후 빈등록
  3. @Test 설정하여 테스트 케이스 작성

Given(request body 생성)

Dto객체를 Json객체로 변환
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com", "홍길동", "010-1234-5678")
String content = gson.toJson(Post)

When(핸들러 메서드 실행)

ResultActions actions = mockMvc.perform()
[MockMvc 객체에 포스트 객체를 넣어 실행, ResultActions 객체 리턴]
포스트 객체 설정
post("/v11/members") [request URL 설정]
.accept(MediaType.APPLICATION_JSON) [responseType 설정]
.contentType(MediaType.APPLICATION_JSON) [requestType 설정]
.content(content)) [포멧된 requestbody 설정]

Then(핸들러 메서드 리턴값 검증)

핸들러 메서드 리턴값(HTTP Status, response body) 검증(then)
actions ResultActions 객체
.andExpect(status().isCreated()) status가 201(Created)가 맞는지
.andExpect(header().string("Location", is(startsWith("/v11/members/"))))
header Location값이 맞는지

@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest {
 @Autowired
 private MockMvc mockMvc;

 @Autowired
 private Gson gson;

 @Test
  void postMemberTest() throws Exception {
    MemberDto.Post post = new MemberDto.Post("hgd@gmail.com", "홍길동", "010-1234-5678");
    String content = gson.toJson(post);
    
    ResultActions actions = mockMvc.perform(  
    								post("/v11/members")
                                    .accept(MediaType.APPLICATION_JSON)      
                                    .contentType(MediaType.APPLICATION_JSON)
                                    .content(content));

	actions.andExpect(status().isCreated())
           .andExpect(header().string("Location", is(startsWith("/v11/members/"))));
    }
}

Controller(Get) 테스트

Given(request body 생성)

앞서 Post된 내용과 해당 location(get요청 임으로 주소값과 파라미터만 있으면 됨)
String location = postActions.andReturn().getResponse().getHeader("Location");

When/Then(핸들러 메서드 실행/리턴값 검증)

mockMvc.perform(get(location).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) 200 OK인지를 검증
.andExpect(jsonPath("$.data.email").value(post.getEmail()))
.andExpect(jsonPath("$.data.name").value(post.getName()))
.andExpect(jsonPath("$.data.phone").value(post.getPhone()));
jsonPath().value()로 Json 포멧된 response body의 프로퍼티가 앞전 포스트의 그것과 일치하는지 검증

@Transactional
@SpringBootTest
@AutoConfigureMockMvc
class MemberControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Gson gson;
    (...)
    
@Test
void getMemberTest() throws Exception {
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com","홍길동","010-1111-1111");
String postContent = gson.toJson(post);

ResultActions postActions = mockMvc.perform(
                        post("/v11/members")
                        .accept(MediaType.APPLICATION_JSON)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(postContent));
//==========================================================================
String location = postActions.andReturn().getResponse().getHeader("Location"); 

mockMvc.perform(get(location).accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.email").value(post.getEmail()))
                .andExpect(jsonPath("$.data.name").value(post.getName())) 
                .andExpect(jsonPath("$.data.phone").value(post.getPhone()));
    }
}

JSON 데이터의 한글이 깨질경우, application.yml 설정을 추가

server:
  servlet:
    encoding:
      force-response: true
      ```
profile
갭린이 리나

0개의 댓글