package com.sooho.projectboard.controller;
import org.junit.jupiter.api.Disabled;
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.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DisplayName("Data REST - API 테스트")
@Transactional
@AutoConfigureMockMvc
@SpringBootTest
class DataRestTest {
private final MockMvc mvc;
DataRestTest(@Autowired MockMvc mvc) {
this.mvc = mvc;
}
@DisplayName("[api] 게시글 리스트 조회")
@Test
void givenNothing_whenRequestingArticles_thenReturnsArticlesJsonResponse() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/articles"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
}
@DisplayName("[api] 게시글 단건 조회")
@Test
void givenNothing_whenRequestingArticle_thenReturnsArticleJsonResponse() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/articles/1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
}
@DisplayName("[api] 게시글 -> 댓글 리스트 조회")
@Test
void givenNothing_whenRequestingArticleCommentsFromArticle_thenReturnsArticleCommentsJsonResponse() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/articles/1/articleComments"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
}
@DisplayName("[api] 댓글 리스트 조회")
@Test
void givenNothing_whenRequestingArticleComments_thenReturnsArticleCommentsJsonResponse() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/articleComments"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
}
@DisplayName("[api] 댓글 단건 조회")
@Test
void givenNothing_whenRequestingArticleComment_thenReturnsArticleCommentJsonResponse() throws Exception {
// Given
// When & Then
mvc.perform(get("/api/articleComments/1"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
}
}
이 코드는 Spring 프레임 워크를 사용하여 RESTful API의 통합 테스트를 수행 하는것이다.
이코드는 DataRestTest 클래스에 있는 여러 개의 API엔드포인트를 호출하고 응답을 확인하는 테스트 케이스를 포함한다.
@Transactional :
어노테이션은 데이터베이스 트랜잭션을 관리하는 데 사용된다. 이 어노테이션은 메서드 레벨이나 클래스 레벨에서 사용할 수 있으며 메서드나 클래스에 붙은 경우 해당 메서드나 클래스의 모든 메서드에 트랜잭션 기능을 적용.
주로 Spring 프레임워크에서 사용되며 데이터베이스와 상호 작용 시 트랜잭션을 시작하고 해당 작업을 커밋(데이터베이스에 변경 내용을 영구적으로 적용) 하거나 롤백(이전상태로 복원) 하는데 사용 된다.
@Transactional 을 사용하면 여러 개의 데이터베이스 조작을 하나의 단위로 묶어서 모든 조작이 성공하거나 실패할 때 롤백할 수 있다. 예를 들어 , 하나의 메서드에서 여러 데이터베이스 조작이 수행되는 경우 이 어노테이션을 사용하여 해당 메서드 내에서 일어나는 모든 작업을 하나의 트랜잭션으로 처리할 수 있다.
따라서 @Transactional 은 데이터베이서 작업의 일관성과 안전성을 유지하기 위해 사용되며 예기치 않은 에러가 발생했을 때 데이터베이스를 이전 상태로 롤백할 수 있도록 도와준다.
@AutoConfigureMockMvc :
어노테이션은 Spring Boot 테스트에서 MockMvc 인스턴스를 자동으로 구성하도록 지시
MockMvc는 Spring MVC 애플리케이션을 테스트할 때 사용되는 테스트 클래스이다. 실제 HTTP 요청을 보내는 대신에 MockMvc를 사용하여 가짜 (Mock) HTTP 요청을 생성하고 컨트롤러가 예상된 대로 작동하는지를 테스트 할 수 있다.
이를 통해 통합 테스트를 수행하면서도 실제 서버를 실행하지 않고 컨트롤러 계층을 테드트 할 수 있다.
mvc.perform(get("/api/articles"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.valueOf("application/hal+json")));
mvc.perform(get("/api/articles")) :
MockMvc를 사용하여 /api/article 엔드포인트에 GET 요청을 보낸다 .get()메서드는 HTTP GET 요청을 생성하고 이를 perform() 메서드를 통해 실행한다.
.andExpect(status().isOk()) :
수행한 요청에 대한 응답 상태 코드가 200 OK인지를 검증한다 isOK() 는 HTTP 응답 코드가 200인지를 확인하는 메서드이다.
.andExpect(content().contentType(MediaType.valueOf("application/hal+json"))) :
수행한 요청에 대한 응답의 컨텐츠 타입이 application/hal+json 인지를 확인하고 contentType() 메스드를 사용하여 MIME 타입이 해당 값인지를 검증한다.
여기서 MIME 란 ?
이메일 첨부 파일 및 컨텐츠 유형 지정: 이메일 시스템은 MIME을 사용하여 이메일의 텍스트, 이미지, 오디오, 비디오, 문서 등과 같은 다양한 유형의 첨부 파일 및 컨텐츠를 지원
웹에서의 데이터 전송: HTTP 통신에서도 MIME은 전송되는 데이터의 종류를 정의하고 예를 들어, HTML 문서, 이미지, 오디오, 비디오 등의 다양한 미디어 유형을 MIME 타입을 통해 전송한다
MIME 타입은 일반적으로 타입/서브타입의 형식을 가집니다. 예를 들어 :
text/plain: 일반 텍스트 문서를 나타내는 MIME 타입
image/jpeg: JPEG 이미지를 나타내는 MIME 타입
application/json: JSON 데이터를 나타내는 MIME 타입
audio/mpeg: MP3 오디오 파일을 나타내는 MIME 타입
MIME 타입은 데이터의 형식을 식별하여 올바른 응용 프로그램이나 시스템으로 전달되도록 지정하는 데 사용되며 웹 브라우저와 서버 간에 데이터 교환 시 MIME 타입은 어떤 종류의 데이터가 전송되고 있는지를 명시하여 올바르게 처리될 수 있도록 한다.
즉, 이 코드는 /api/articles 엔드포인트에 GET 요청을 보내고, 서버에서 받은 응답이 HTTP 상태 코드가 200 OK이며, 컨텐츠 타입이 application/hal+json인지를 검증하는 테스트를 수행한다.
최종 내가 이 테스트 코드를 사용한 이유?
1. API 엔드포인트의 기본 동작 검증 :
각 테스트는 주어진 엔드포인트에 대한 기대한 대로 동작하는지를 확인하고 예를 들어 게시글 리스트 조회 ,단일 게시글 조회, 게시글의 댓글 리스트 조회, 댓글 리스트 조회 및 단일 조회와 같이 다양한 API 엔드포인트 동작을 확인 하고 싶었다.
2. 상태 코드 및 컨텐츠 유형 확인 :
각 테스트는 해당 API 엔드포인트에 대한 요청 후 응답의 상태 코드가 올바른지 (isok()) 와 응답의 컨텐츠 유형이 예상 대로인지 (application/hal+json)를 확인하고 싶었다.
3. 트랜잭션 설정 :
@Transactional 어노테이션을 사용하여 테스트 메서드 트랜잭션으로 감싸고 롤백을 수행하므로 각 테스트는 트랜잭션 내에서 실행되고 데이터베이스에 영향을 미치지 않고 실행을 완료 하고싶었다.
다만
이것은 이 프로젝트의 비즈니스 로직으로 구현한 내용이 아니라 data rest 기능이고,
통합테스트라 무거우며 db 에도 영향을 준다.
공부목적으로 의미가 있기 때문에 삭제하지는 않고 제외 처리해서 전체 테스트 중에 실행되지 않게끔 처리
예외 처리는 @Disabled 할것이다.
'프로젝트 > 게시판 서비스' 카테고리의 다른 글
3. 데이터베이스 접근 로직 구현 (0) | 2023.11.30 |
---|---|
2. 데이터베이스 접근 로직 테스트 정의 (1) | 2023.11.30 |
1-2 프로젝트 기획 (필요한 기술 정리) (0) | 2023.11.24 |
1-1 프로젝트 기획 (환경,목적) (1) | 2023.11.24 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!