DevKim

[스프링 with AWS] MockMvc를 이용한 API 테스트 코드 본문

Spring Boot

[스프링 with AWS] MockMvc를 이용한 API 테스트 코드

on_doing 2021. 7. 10. 11:46
728x90

오늘 드디어 이동욱님의 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 책을 읽을 수 있는 시간이 생겼다.

이전에서도 Test code 작성시 Junit4가 익숙했는데, 이 책에서도 Junit4를 사용한다고해서 다행이라고 생각했다.

현재 버전 이슈가 많은 것 같은데, 패키지에 대해 잘 알고있다면 어렵지 않게 고칠 수 있는 에러들인 것 같다.


🏃 CH.01 🏃

우선 Junit4를 사용하기 위해선 아래를 build.gradle에 추가해주어야한다.

//JUnit4 추가
testImplementation("org.junit.vintage:junit-vintage-engine") { 
exclude group: "org.hamcrest", module: "hamcrest-core" 
}

모든 책의 기본인 hellocontroller를 생성해주고 서버가 잘 띄워지는지 확인해보려고 하는 찰나에,

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello()
    {
        return "hello";
    }
}

그 다음 줄에 test 코드를 작성하자고 나온다.

이렇게 간단한걸 test 코드를 작성하자구요?! 라고 처음엔 생각했지만..

그 다음 줄을 읽어보며 지금까지 아무렇지 않다고 생각했던 습관을 간파당했다 ㅎ ㅎ

 

매번 프로젝트를 실행할 때,

나는 이러한 프로세스로 진행했던 것 같다.

1. 코드 작성하고
2. tomcat 실행한 후
3. ARC로 HTTP 요청해서 잘 받아와지는지 확인하고
4. 테스트 코드 작성하고
5. 결과가 다르면 다시 tomcat 중지하고 코드를 수정하고..
6. 반복..

이게 좋지 않은 방법이란건 이론적으로 너무 많이 들어서 알고 있었지만 이게 나의 습관이 되었을 줄은 꿈에도 몰랐다.

 

추가로, 수동으로 검증하고 테스트 코드를 작성하지 않습니다. 

라는 말에 tomcat 서버부터 띄우는 습관을 반드시 고쳐야겠다고 생각했다.


🏃 CH.02 🏃

1. 여기서는 MockMvc를 사용하여 test 단에서 API를 테스트하는 방법을 알려준다.

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void hello가_리턴된다() throws Exception{
        //given
        String hello="hello";

        //then
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }
}

📌 @WebMvcTest 

: Web (Spring MVC)에 집중할 수 있는 어노테이션

: 선언 할 경우 @Controller/@ControllerAdvice는 사용할 수 있지만,  @service @component, @Repository등은 사용할 수 없다

 

📌 MockMvc

: 웹 API를 테스트할 때 사용

 

📌 mockMvc.perform(get("/hello"))

: MockMvc를 통해 /hello 주소로 GET 요청

 

📌 .andExpect(status.isOk())

: perform의 결과를 검증 = HTTP Header의 Status 검증

 

📌 .andExpect(content().string(hello))

: perfrom의 결과를 검증 = 응답 본문의 내용을 검증


※ 만약, 셋중에 하나가 alt+enter로 import가 나오지 않는다면 밑에 셋중에 하나를 추가해주자.

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

2. HelloController 코드를 lombok으로 전환하기

 

📌 정말 자주 쓰는 Getter와 Require-를 사용한 DTO를 추가해주었다.

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {

    private final String name;
    private final int amount;
}

📌 당연히 롬복이 잘 작동하는지 이것또한 test code로 검증해준다.

@Test
public void test() throws Exception{
	//given
	String name="test";
	int amount=1000;

	//when
	HelloResponseDto dto = new HelloResponseDto(name,amount);

	//then
	assertThat(dto.getName()).isEqualTo(name);
	assertThat(dto.getAmount()).isEqualTo(amount);
}

코드상에서 딱히 보고 넘어가야할만큼 새로운 것은 없었고,

 

매번 Junit말고 assertj의 assertion을 static import 해서 사용했었는데 정작 그 장점은 잘 모르고 사용했던 것 같다.

 

 1. Junit의 assertThat을 쓰게 되면 is()와 같이 CoreMatchers 라이브러리가 필요한데, assertj는 추가적인 라이브라리가 필요없다.
2. 자동완성이 좀 더 확실하게 지원된다.

 

사실 Juint의 assertThat을 사용해본적이 없어서 확 와닿진 않지만,

누군가 왜 assertj 사용하냐고 물으면 이젠 대답할 수 있을 것 같다.


📌 이제 Controller에 생성한 DTO를 사용하도록 코드를 추가해보자

@RestController
public class HelloController {
    @GetMapping("/hello")
    public HelloResponseDto hello(@RequestParam("name") String name, @RequestParam("amount") int amount)
    {
        return new HelloResponseDto(name,amount);
    }
}

📌 Test code 작성

@Test
public void helloDto가_리턴된다() throws Exception{
	//given
	String name="hello";
	int amount=1000;

	//then
	mockMvc.perform(
			get("/hello/dto")
				.param("name",name)
				.param("amount",String.valueOf(amount)))
			.andExpect(status().isOk())
			.andExpect(jsonPath("$.name",is(name)))
			.andExpect(jsonPath("$.amount",is(amount)));

}

※ is 오류 -> import 바꿔주기

import static org.hamcrest.Matchers.is;

📌 .param("amount",String.valueOf(amount))

: API 테스트시 요청 파라미터 설정

: 단, 값은 String만 허용

 

📌 .andExpect(jsonPath("$.name",is(name)))

: JSON 응답값을 필드별로 검증할 수 있는 메소드

: $를 기준으로 필드명을 명시


그 다음 장에선 JPA를 사용해야하는 이유와, JPA의 러닝커브에 대한 이야기가 나온다.

사실 처음부터 Spring Data Jpa를 먼저 접했기 때문에, 처음엔 Jpa를 공부하는게 복잡하고 귀찮게만 느껴졌다.

(원래 책 한권 읽은 사람이 안 읽은 사람보다 무식한 것.. ㅎㅎ)

 

하지만 이 생각은 김영한님의 Jpa강의를 들으며 전부 뒤바뀌었다.

Jpa를 나름 깊게 공부하고 원리를 알고나니,

이게 왜 이렇게 되고 이 어노테이션을 왜 여기에서 쓰는지, 왜 여기서 이런 쿼리가 나가는지를 알 수 있었다.

 

그래서 Spring 공부하시는 분들을 만날 때마다, 김영한님의 Jpa는 꼭꼭 들으라고 전파중이다. (좋은건 같이,,)

그만큼 JPA를 알고 Data JPA를 쓰는 것과 JPA를 모르는 상태에서 Data JPA를 쓰는 것은 텅 빈 성에 모래를 쌓는 것과 같은 느낌인 것 같다.

 

책을 읽다보니 오전 9시에 시작해서 2시간만에 책의 3/1을 완독했다.

글이 참 술술 잘 읽히는 좋은 책인 것 같다.

728x90
Comments