일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 테스트메소드
- EC2
- appspec.yml
- 커밋메세지수정
- 테스트
- foreignkey
- ㅔㄴ션
- 포트
- WeNews
- 추후정리
- appspec
- 외부키
- 예약
- querydsl
- subquery
- ubuntu
- 참조키
- 검색
- Query
- MySQL
- 컨테이너실행
- docker명령어
- application.yml
- 2 > /dev/null
- 적용우선순위
- 메소드명
- 네이티브쿼리
- 메세지수정
- AuthenticationEntryPoint
- 서브쿼리
- Today
- Total
제뉴어리의 모든것
[Section3] [Spring MVC] 테스팅(Testing) - 2 (API 계층 테스트) 본문
아래의 내용들을 원활히 이해하기 위해선
https://januaryman.tistory.com/458?category=959308
위에 포스트에 있는 기본적인 애노테이션을 알아두는것이 좋다.
build.gradle
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.codestates'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.mapstruct:mapstruct:1.5.1.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'com.google.code.gson:gson'
}
tasks.named('test') {
useJUnitPlatform()
}
API 계층 테스트
API 계층 테스트의 대상은 주로 클라이언트의 요청을 받아들이는 Controller 이다.
- API 계층 테스트 기본 구조
import com.google.gson.Gson;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest //(1)
@AutoConfigureMockMvc //(2)
class MemberControllerTest {
@Autowired
private MockMvc mockMvc; //(3)
@Autowired
private Gson gson; //(4)
@Test
void postMemberTest() throws Exception {
// given
MemberDto.Post post = new MemberDto.Post("hgd@gmail.com", //(5)
"홍길동",
"010-1234-5678");
String content = gson.toJson(post); //(6)
// when
ResultActions actions =
mockMvc.perform( //(7)
post("/v11/members")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content)
);
// then
MvcResult result = actions //(8)
.andExpect(status().isCreated())
.andReturn();
// System.out.println(result.getResponse().getContentAsString()); // (9)
}
}
(1) @SpringBootTest : 위에 포스트에서 잘 나와있지만, 앱 자체를 띄우는 환경의 테스트를 하겠다는것이다.
(2) @AutoConfigureMockMvc : 의존성 주입을 받고 있는 MockMvc에 대한 설정을 자동으로 해주겠다는 것이다.
사실 정말 Controller단만의 테스트를 하고 싶다면
@SpringBootTest 애노테이션 대신에
@WebMvcTest 애노테이션을 쓰는것이 맞다고 생각한다.
왜냐면 @SpringBootTest 애노테이션을 사용하면 테스트 하려는 Controller 뿐만 아니라, Spring 기본 빈들과 내가 정의한 빈들 또한 모두 등록이 되기 때문이다.
하지만
@WebMvcTest(MemberController.class) 이런식으로 매개변수에 넣은 테스트할 Controller만 빈으로 등록할 수 있다.
(3) : Controller 계층으로 request를 보낼 수 있게 해주는 MockMvc 객체를 선언하고 주입받았다.
@AutoConfigureMockMvc 애노테이션의 기능으로 MockMvc는 빈으로 등록되고 내부 설정까지 자동으로 이루어진다.
그러므로 @Autowired가 가능한것이다.
(4) : 객체를 Json 구조의 String으로 변환 시켜주는 라이브러리이다.
#의문 : Gson 클래스는 어디에서 빈등록을 해주는것일까?
(5) : 테스트를 위해 RequestBody에 담을 DTO 객체의 선언
(6) : DTO 객체를 JSON 구조의 String으로 변환
(7) : MockMvc 객체의 perform() 메소드를 이용하여 post 메소드를 보내는 내용이다.
perform() 메소드는 결국 RequestBuilder 타입의 인자를 받는 메소드이다.
그 안에 있는 patch, accept 등의 메소드는 RequestBuilder를 리턴하는 static 메소드인 것이다.
.accept, .contentType은 헤더 내용 추가임.
(8) : (7) 에서 보낸 Request에 대한 Response 내용을 담은 ResultAction 객체를 가지고 Response 내용에 대해서 Expect를 하여 Controller 핸들러 메소드의 결과를 검증하는 부분이다.
(9) : 해당 코드처럼 Controller가 반환한 Response에 대해서 그대로 콘솔에 찍어볼 수 있다.
그러나, 한글 내용이 깨져서 나올 수가 있다.
그럴경우, 아래의 내용을 application.yml 파일 (application.properties) 최상위 루트에 넣어주면 된다.
server: // 해당 라인 부터 제일 밑에 라인까지 한글 깨짐 방지
servlet:
encoding:
force-response: true
- 예시
spring:
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create # (1) 스키마 자동 생성
show-sql: true # (2) SQL 쿼리 출력
properties:
hibernate:
format_sql: true # (3) SQL pretty print
sql:
init:
data-locations: classpath*:db/h2/data.sql
logging:
level:
org:
springframework:
orm:
jpa: DEBUG
server: // 해당 라인 부터 제일 밑에 라인까지 한글 깨짐 방지
servlet:
encoding:
force-response: true
+ Gson 사용 하려면
build.gradle에 의존성을 아래와 같이 추가해줘야함.
dependencies {...}에 implementation 'com.google.code.gson:gson'
+ perform() 메소드 안에 들어가는 post, get 같은 메소드를 사용하려면
org.springframework.test.web.servlet.request.MockMvcRequestBuilder 패키지를 import 시켜줘야한다.
- 예시
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
:
:
Controller의 Response 내용 추가 검증
MvcResult result = actions
.andExpect(status().isCreated())
.andExpect(jsonPath("$.data.email").value(post.getEmail())) // (1)
.andExpect(jsonPath("$.data.name").value(post.getName())) // (2)
.andExpect(jsonPath("$.data.phone").value(post.getPhone())) // (3)
.andReturn();
andExpect메소드 내에 jsonPath() 메소드를 이용하여
data 내용을 검증 할 수 있다.
참고로 현재 Controller로 부터 넘어 온 Json데이터는 아래의 구조와같다.
{"data":{"memberId":1,"email":null,"name":"준","phone":null,"memberStatus":"활동중","stamp":0}}
$.data.email 의 해석 : $은 json 객체 자체를 의미하며, 해당 객체에 있는 필드에 접근하려면 . 을 찍어 접근한다.
그리고 data 안에 있는 email 필드를 접근하려면, 또 . 을 찍어서 email에 접근한다.
이렇게 json 객체에 접근하는 방법에는
위와 같은
- Dot 표현법
- Bracket 표현 법이 있다.
데이터에 접근하는 자세한 방법은 아래의 블로그를 참조하자.
+ jsonPath
jsonPath는 MockMvcResultMatchers 클래스에 존재하는 정적 메소드로써
Json 객체의 데이터를 탐색할 수 있게 해준다.
참조 : https://joojimin.tistory.com/52
결론 :
위에 테스트는 API 테스트라고 했지만,
Controller만을 테스트 하는 상황이 아니다.
@SpringBootTest 를 사용하여 앱내의 모든 빈들을 등록한 상태이며,
서버로 Request를 보내면 Service 계층, Repository까지 모두 거친다.
그러므로, @MockBean 등을 이용하여 Service 계층과의 연결을 끊어줘야한다.
추후 [Section3] [Spring MVC] 테스팅(Testing) 게시물에서 보도록 하자.
위에 내용들을 이용한 Git Hub 프로젝트 : https://github.com/JanuaryKim/be-homework-testing-slice
'코드스테이츠 > 정리 블로깅' 카테고리의 다른 글
[Section3] [Spring MVC] 테스팅(Testing) - 3 (Mockito) Service 계층을 끊은 Controller 테스트 (0) | 2022.09.17 |
---|---|
[Section3] [Spring MVC] 테스팅(Testing) - 3 (데이터 액세스 계층 테스트) (0) | 2022.09.17 |
[Section3] [Spring MVC] 트랜잭션(Transaction) (0) | 2022.09.07 |
[Section3] [Spring MVC] JPA - 1 [추후 정리] (0) | 2022.09.03 |
[Section3] [Spring MVC] JDBC 기반 데이터 액세스 계층 - 2 (0) | 2022.08.27 |