[Java] Spring Boot: 테스트 코드
이전 내용
[Java] Spring Boot: 방명록 Rest API 구현(feat. MySQL 연동)
이전 내용 [Java] Spring Boot: 코드, 요청&응답 과정 이해하기이전 내용 [Java] Spring Boot: Maven Repository이전 내용 [Java] 스프링부트: Spring Initializr - Dependencies이전 내용 [Java] Spring Boot: 제어 역전, 의존성
puppy-foot-it.tistory.com
테스트 코드
앞서 CRUD 기능이 가능한 방명록 Rest API를 구현하는 작업을 진행했다.
그 중, 테스트 코드에 관해 좀 더 상세하게 설명하려 한다.
[테스트 코드 필요성]
수동 테스트는 개발자가 직접 테스트를 수행하는 것으로, 코드 수동 테스트의 주요 문제는 반복적인 작업과 시간 소요, 인간 오류 가능성, 그리고 모든 경우를 검증하기 어려움 등이 있다. 또한, 수동 테스트는 자동화 테스트에 비해 비용이 많이 들고 효율성이 떨어질 수 있다.
이러한 문제를 방지하고 개선하기 위해 테스트 코드가 필요하다. 테스트 코드는 소프트웨어 개발의 필수적인 부분으로, 품질을 보장하고 안정성을 높이며, 코드 이해도를 높이고, 개발 효율성을 향상시키며, 비용 절감에 기여한다. 이러한 이유들 때문에 대부분의 소프트웨어 프로젝트에서는 테스트 코드 작성을 권장하고 있다.
JUnit
자바를 위한 단위 테스트 프레임워크로, 메소드, 클래스 및 패키지에 대한 단위 테스트를 작성하는 데 자주 사용된다.
- 간단하고 사용하기 쉬운 API를 제공하며, 테스트 케이스를 작성하고 실행하는 데 필요한 다양한 어노테이션 및 메서드를 제공한다.
- 테스트의 성공, 실패 또는 예외 처리와 같은 결과를 자동으로 보고한다.
- 개발자는 코드를 작성하기 전에 해당 코드의 동작을 정의하는 테스트 케이스를 작성하고, 이 테스트 케이스를 사용하여 코드를 구현하고 테스트한다. 이를 통해 코드의 품질을 향상시키고 버그를 빠르게 발견할 수 있다.
※ 단위 테스트(Unit Test)
- 작은 코드 단위를 독립적으로 검증하는 테스트
- 소프트웨어의 가장 작은 단위인 모듈, 함수, 클래스 등의 개별적인 기능을 테스트하는 것
- 주로 프로그래머가 작성하며, 코드의 동작을 검증하고 예상대로 동작하는지 확인한다.
- 코드 변경 시에 기존 코드의 충돌 및 오류를 발견 가능하며, 새로운 코드의 동작의 성공 여부를 확인할 수 있다.
- TDD에서의 테스트 케이스는 주로 단위 테스트 작성을 의미한다. 그리고 주로 자동화되어 사용된다(CI).
[CI / CD]
- CI, Continuous Integration (지속적 통합): 개발자들이 작업한 코드를 지속적으로 통합하여 통합 문제를 최소화하고, 코드 변경이 일어날 때마다 자동으로 빌드 및 테스트가 실행되며, 코드베이스에 통합시키도록 한다.
- CD, Continuous Deployment (지속적 배포) : CI를 통과한 이후, 자동화된 프로세스를 통해 테스트를 통과한 코드를 실제 환경으로 자동으로 프로덕션 환경에 배포하는 파이프라인 구축을 목적으로 한다
[자주 사용하는 JUnit 애너테이션]
@BeforeAll 애너테이션
- 전체 테스트를 시작하기 전에 처음으로 한 번만 실행. 예를 들어 데이터베이스를 연결해야 하거나 테스트 환경을 초기화할 때 사용된다. 이 애너테이션은 전체 테스트 실행 주기에서 한 번만 호출되어야 하기 때문에 메서드를 static으로 선언해야 한다.
@BeforeEach 애너테이션
- 테스트 케이스를 시작하기 전에 매번 실행. 예를 들어 테스트 메서드에서 사용하는 객체를 초기화하거나 테스트에 필요한 값을 미리 넣을 때 사용할 수 있다. 각 인스턴스에 대해 메서드를 호출해야 하므로 메서드는 static이 아니어야 한다.
@AfterAll 애너테이션
- 전체 테스트를 마치고 종료하기 전에 한 번만 실행. 예를 들어 데이터베이스 연결을 종료할 때나 공통적으로 사용하는 자원을 해제할 때 사용할 수 있다. 전체 테스트 실행 주기에서 한 번만 호출되어야 하므로 메서드를 static으로 선언해야 한다.
@AfterEach 애너테이션
- 각 테스트 케이스를 종료하기 전 매번 실행. 예를 들어 테스트 이후에 특정 데이터를 삭제해야 하는 경우 사용. @BeforeEach 애너테이션과 마찬가지로 메서드는 static이 아니어야 한다.
AssertJ
AssertJ: 검증문의 가독성을 높여주는 라이브러리.
[AssertJ의 자주 사용하는 메소드]
메소드 이름 | 설명 |
isEqualTo(X) | X 값과 같은지 검증 |
isNotEqualTo(X) | X 값과 다른지 검증 |
contains(X) | X 값을 포함하는지 검증 |
doesNotContain(X) | X 값을 포함하지 않는지 검증 |
startsWith(X) | 접두사가 X인지 검증 |
endsWith(X) | 접미사가 X인지 검증 |
isEmpty() | 비어 있는 값인지 검증 |
isNotEmpty() | 비어 있지 않은 값인지 검증 |
isPostive() | 양수인지 검증 |
isNegative() | 음수인지 검증 |
isGreaterThan(1) | 1보다 큰 값인지 검증 |
isLessThan(1) | 1보다 작은 값인지 검증 |
given - when - then 패턴
given - when - then 패턴은 테스트 코드 작성에서 널리 사용되는 구조로, 테스트의 가독성과 이해도를 높이는 데 도움을 준다. 이 패턴은 테스트의 각 단계를 명확히 구분하여, 테스트가 무엇을 하고 있는지를 쉽게 파악할 수 있도록 한다.
1. Given: 테스트를 수행하기 위한 초기 조건이나 설정을 정의. 테스트할 객체나 상태를 준비하는 과정
- 데이터베이스에 특정 데이터가 존재하도록 설정하기
- API 요청을 위한 초기화 작업
- 객체의 상태를 특정 값으로 설정하기
2. When: 실제로 테스트하고자 하는 행위를 수행. 주어진 조건을 바탕으로 특정 메소드를 호출하거나 요청을 전송하는 과정을 포함
- API 요청을 보내는 코드
- 특정 메소드를 호출하여 상태를 변경하는 작업
3. Then: 기대하는 결과를 검증. 주어진 조건에서 발생해야 하는 결과를 확인하며, 테스트가 성공적인지를 판단하는 부분
- 응답 상태 코드 검증
- 데이터베이스 상태 검증
- 반환된 값을 비교하거나 단언하는 작업
테스트 파일 생성
테스트를 할 때는 보통 main에 있는 파일의 클래스를 alt+enter를 누르면 생성할 수 있다.
테스트 생성을 선택하면 src/test/java 내에 원래 파일명+Test.java 라는 파일에 생성된다.
앞서, 방명록을 만들 때 만든 테스트 코드는 아래와 같다.
package com.example.guestBook.controller;
import com.example.guestBook.domain.Article;
import com.example.guestBook.dto.AddArticleRequest;
import com.example.guestBook.dto.UpdateArticleRequest;
import com.example.guestBook.repository.GuestBookRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
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.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@SpringBootTest // 테스트용 애플리케이션 컨텍스트
@AutoConfigureMockMvc // MockMvc 생성
class GuestBookApiControllerTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@Autowired
private WebApplicationContext context;
@Autowired
GuestBookRepository guestBookRepository;
@BeforeEach // 테스트 실행 전 실행 메소드
public void mockMvcSetup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
guestBookRepository.deleteAll();
}
@DisplayName("addArticle: 방명록 추가 성공")
@Test
public void addArticle() throws Exception {
//given
final String url = "/api/guestbook";
final String author = "author";
final String content = "content";
final AddArticleRequest userRequest = new AddArticleRequest(author, content);
// 객체 JSON으로 직렬화
final String requestBody = objectMapper.writeValueAsString(userRequest);
// when
// 설정한 내용 바탕으로 요청 전송
ResultActions result = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(requestBody));
//then
result.andExpect(status().isCreated());
List<Article> articles = guestBookRepository.findAll();
assertThat(articles.size()).isEqualTo(1); // 크기가 1인지 검증
assertThat(articles.get(0).getAuthor()).isEqualTo(author);
assertThat(articles.get(0).getContent()).isEqualTo(content);
}
@DisplayName("findAllArticles: 방명록 글 목록 조회 성공")
@Test
public void findAllArticles() throws Exception {
//given
final String url = "/api/guestbook";
final String author = "author";
final String content = "content";
guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
//when
final ResultActions resultActions = mockMvc.perform(get(url)
.accept(MediaType.APPLICATION_JSON_VALUE));
//then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].content").value(content))
.andExpect(jsonPath("$[0].author").value(author));
}
@DisplayName("findArticle: 방명록 글 조회 성공")
@Test
public void findArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
// when
final ResultActions resultActions = mockMvc.perform(get(url, savedArticle.getId()));
// then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").value(content))
.andExpect(jsonPath("$.author").value(author));
}
@DisplayName("deleteArticle: 방명록 글 삭제 성공")
@Test
public void deleteArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
//when
mockMvc.perform(delete(url, savedArticle.getId()))
.andExpect(status().isOk());
//then
List<Article> articles = guestBookRepository.findAll();
assertThat(articles).isEmpty();
}
@DisplayName("updateArticle: 블로그 글 수정 성공")
@Test
public void updateArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
final String newAuthor = "new Author";
final String newContent = "new Content";
UpdateArticleRequest request = new UpdateArticleRequest(newAuthor, newContent);
// when
ResultActions result = mockMvc.perform(put(url, savedArticle.getId())
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(request)));
//then
result.andExpect(status().isOk());
Article article = guestBookRepository.findById(savedArticle.getId()).get();
assertThat(article.getAuthor()).isEqualTo(newAuthor);
assertThat(article.getContent()).isEqualTo(newContent);
}
}
각 기능별 테스트 코드 설명
1. 의존성 주입
@SpringBootTest // 테스트용 애플리케이션 컨텍스트
@AutoConfigureMockMvc // MockMvc 생성
class GuestBookApiControllerTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@Autowired
private WebApplicationContext context;
@Autowired
GuestBookRepository guestBookRepository;
@BeforeEach // 테스트 실행 전 실행 메소드
public void mockMvcSetup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
guestBookRepository.deleteAll();
}
}
- 애너테이션:
- @SpringBootTest: Spring Boot 애플리케이션의 테스트 환경 설정.
- @AutoConfigureMockMvc: MockMvc를 자동으로 설정하여 HTTP 요청을 모방할 수 있게 한다.
- 의존성 주입:
- @Autowired: Spring에 의해 자동으로 의존성을 주입한다. 즉, 이 필드들이 애플리케이션의 다른 부분에서 생성된 객체들을 자동으로 연결해 줌.
- MockMvc: HTTP 요청을 테스트 하는 데 사용.
- ObjectMapper: JSON과 Java 객체 간의 변환을 도와주는 라이브러리. Java 객체를 JSON으로 직렬화 및 역직렬화하는 데 사용. ▶ 예를 들어, Java 객체를 JSON 형식으로 바꾸거나 그 반대의 작업을 할 수 있다.
- WebApplicationContext: Spring의 웹 애플리케이션 설정 정보를 포함하는 객체로, 웹 환경에서 애플리케이션 컨텍스트 제공. ▶ MockMvc 설정
- GuestBookRepository: 데이터베이스와 상호작용하는 리포지토리. 방명록 데이터를 저장하고 조회하는 데 필요한 클래스.
- 테스트 설정:
- @BeforeEach: 각 테스트 메소드가 실행되기 전에 항상 호출되는 메소드. 따라서, 여기서는 MockMvc를 설정하고, 테스트 중에 데이터베이스를 초기화(모든 데이터를 삭제)하며, 이를 통해 각 테스트는 독립적으로 실행되며 서로 영향을 받지 않는다.
- mockMvcSetup(): 매 테스트 실행 전에 MockMvc를 설정하고, 데이터베이스를 초기화하여 테스트의 독립성 보장.
※ MockMvc: 개발한 웹 프로그램을 실제 서버에 배포하지 않고도 테스트를 위한 요청을 제공하는 수단
테스트 코드 공통 항목
- @DisplayName: 이 어노테이션은 테스트 메소드의 설명을 붙여준다. 이 설명은 테스트 결과를 확인할 때 유용하다.
- @Test: 이 어노테이션은 JUnit에게 이 메소드가 테스트 메소드임을 알려준다.
- given - when - then 패턴
- andExpect: ResultActions 객체에 사용되어, 요청에 대한 기대 결과를 정의하는 데 사용. 이 메소드를 통해 응답 상태 코드, 응답 본문, 헤더 등 검증 가능. ▶ status().isOk(): 상태 코드가 200 OK인지 확인. / jsonPath(...): JSON 응답의 특정 필드 값을 검증.
- assertThat: JUnit과 AssertJ 라이브러리를 이용한 단언(assertion) 기능 제공. 이 메소드를 사용하여 특정 값이나 상태가 예상한 것과 일치하는지 검증 가능. ▶ 매개변수로 검사할 값을 받고, 이어서 다양한 메소드 체이닝을 통해 조건 검증.
- mockMvc.perform: 실제 HTTP 요청을 모방하여 컨트롤러를 호출하는 역할. 이 메소드를 사용하면 HTTP 메소드(예: GET, POST, PUT, DELETE 등)와 URL을 지정하고, 필요에 따라 요청 본문 및 헤더 설정 가능. ▶ 반환 타입은 ResultActions
각 기능별 테스트 코드 설명
2. 방명록 추가 테스트
@DisplayName("addArticle: 방명록 추가 성공")
@Test
public void addArticle() throws Exception {
//given
final String url = "/api/guestbook";
final String author = "author";
final String content = "content";
final AddArticleRequest userRequest = new AddArticleRequest(author, content);
// 객체 JSON으로 직렬화
final String requestBody = objectMapper.writeValueAsString(userRequest);
// when
ResultActions result = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(requestBody));
//then
result.andExpect(status().isCreated());
List<Article> articles = guestBookRepository.findAll();
assertThat(articles.size()).isEqualTo(1); // 크기가 1인지 검증
assertThat(articles.get(0).getAuthor()).isEqualTo(author);
assertThat(articles.get(0).getContent()).isEqualTo(content);
}
- given: 테스트 환경을 설정하는 부분. 여기서 URL과 방명록의 작성자와 내용을 준비하여 AddArticleRequest 객체 생성.
- objectMapper.writeValueAsString(userRequest): userRequest 객체를 JSON 문자열로 변환하며, 이 과정에서 객체의 각 속성이 JSON의 키-값 쌍으로 변환 됨. userRequest 객체는 일반적으로 방명록에 추가할 정보를 담고 있는 객체.
- when: 실제로 HTTP 요청을 보내는 부분. POST 요청을 통해 방명록을 추가하고, 요청의 본문은 JSON 형식으로 전달.
- mockMvc.perform(post(url): 지정된 URL에 대한 POST 요청 준비
- Content-Type: contentType(MediaType.APPLICATION_JSON_VALUE)를 통해 요청이 JSON 형식임을 서버에 알림.
- content(requestBody)를 통해 요청 본문에 직렬화된 JSON 데이터 추가
- then: 결과 검증 단계. andExpect를 사용하여 응답 상태 코드가 201 Created가 맞는지 확인. 이는 방명록이 성공적으로 추가되었음을 의미.
- assertThat: JUnit의 단언(assertion) 기능을 사용하여, 데이터베이스에 저장된 방명록의 수와 내용 검증. 방명록이 정확히 추가되었는지 확인하는 단계.
각 기능별 테스트 코드 설명
3. 방명록 글 목록 조회 테스트
@DisplayName("findAllArticles: 방명록 글 목록 조회 성공")
@Test
public void findAllArticles() throws Exception {
//given
final String url = "/api/guestbook";
final String author = "author";
final String content = "content";
guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
//when
final ResultActions resultActions = mockMvc.perform(get(url)
.accept(MediaType.APPLICATION_JSON_VALUE));
//then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].content").value(content))
.andExpect(jsonPath("$[0].author").value(author));
}
- Given: /api/guestbook URL과 작성자, 내용을 정의하고, 방명록을 생성하여 데이터베이스에 저장.
- API 엔드포인트 URL, 저자 및 내용 설정
- guestBookRepository.save(...): Article 객체를 생성하여 데이터베이스에 저장
- Article.builder()를 사용하여, 글의 작성자와 내용을 설정한 후, 새 방명록 글을 생성하고 저장
- When: GET 요청을 보낸다. accept 헤더를 설정하여 JSON 형식으로 응답 요청.
- mockMvc.perform(get(url): 설정한 URL에 GET 요청을 보냄.
- .accept(MediaType.APPLICATION_JSON_VALUE): 서버에 응답 형식으로 JSON을 요청. 클라이언트는 JSON 형식의 응답을 기대하고 있다는 것을 명시
- Then: 응답 상태 코드가 200 OK인지 확인하고, JSON 응답에서 첫 번째 글의 내용과 작성자가 올바른지 검증.
- andExpect(status().isOk()): 응답의 상태 코드가 200 OK인지 확인. 이는 요청이 성공적으로 처리되었음을 나타냄.
- andExpect(jsonPath("$[0].content").value(content)): JSON 응답의 첫 번째 배열 요소에서 content 속성이 예상한 값("content")과 일치하는지 확인.
- andExpect(jsonPath("$[0].author").value(author)): JSON 응답의 첫 번째 배열 요소에서 author 속성이 예상한 값("author")과 일치하는지 확인
※ jsonPath: JSON 경로를 통해 JSON 구조를 탐색하며, $[0]는 첫 번째 배열 요소를 의미. 이에 따라 JSON 응답이 배열 형식으로 제공될 때 원하는 필드를 정확히 확인 가능.
각 기능별 테스트 코드 설명
4. 특정 방명록 글 목록 조회 테스트
@DisplayName("findArticle: 방명록 글 조회 성공")
@Test
public void findArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
// when
final ResultActions resultActions = mockMvc.perform(get(url, savedArticle.getId()));
// then
resultActions
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").value(content))
.andExpect(jsonPath("$.author").value(author));
}
- Given: 조회할 URL과 작성자, 내용을 설정한 후, 방명록 글을 데이터베이스에 저장.
- API 엔드포인트 URL, 저자 및 내용 설정
- guestBookRepository.save(...): 방명록 글을 데이터베이스에 저장
- Article.builder(): 작성자와 내용을 설정한 후, 방명록 글 객체를 생성하고 저장
- When: GET 요청을 보내고, 방명록 글의 ID를 URL에 삽입하여 해당 글 조회.
- mockMvc.perform(get(url, savedArticle.getId())): 지정한 URL에 대한 GET 요청 보냄. 이때 savedArticle.getId()를 통해 이전 단계에서 저장한 방명록 글의 ID를 URL에 삽입
- Then: 응답의 상태 코드가 200 OK인지 확인하고, JSON 응답에서 글의 내용과 작성자가 올바른지 검증.
- andExpect(status().isOk()): 응답의 상태 코드가 200 OK인지 확인. 이는 요청이 성공적으로 처리되었음을 나타냄.
- andExpect(jsonPath("$.content").value(content)): JSON 응답에서 content 속성이 예상한 값("content")과 일치하는지 확인.
- andExpect(jsonPath("$[0].author").value(author)): JSON 응답에서 author 속성이 예상한 값("author")과 일치하는지 확인.
※ jsonPath: JSON 경로를 통해 JSON 객체의 속성에 접근. "$"는 최상위 JSON 객체를 의미하며, "." 이후에 지정한 키로 해당 속성의 값을 추출할 수 있다.
각 기능별 테스트 코드 설명
5.방명록 글 삭제 테스트
@DisplayName("deleteArticle: 방명록 글 삭제 성공")
@Test
public void deleteArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
//when
mockMvc.perform(delete(url, savedArticle.getId()))
.andExpect(status().isOk());
//then
List<Article> articles = guestBookRepository.findAll();
assertThat(articles).isEmpty();
}
- Given: 삭제할 방명록 글의 URL과 내용을 준비하고, 해당 글을 데이터베이스에 저장.
- API 엔드포인트 URL, 저자 및 내용 설정
- guestBookRepository.save(...): 방명록 글을 데이터베이스에 저장
- Article.builder(): 작성자와 내용을 설정한 후, 방명록 글 객체를 생성하고 저장
- When: DELETE 요청을 보내고, URL에 방명록 글의 ID를 사용하여 삭제 작업 수행.
- mockMvc.perform(delete(url, savedArticle.getId())): 저장된 방명록 글 ID를 포함한 DELETE 요청 보내는데, savedArticle.getId()를 통해 방금 저장한 방명록 글의 ID를 URL에 포함
- andExpect(status.isOk()): 요청이 성공적으로 처리되었음을 확인 (200 OK 응답)
- Then: 응답의 상태 코드가 200 OK인지 확인하고, 데이터베이스에서 모든 방명록 글이 비어 있는지를 검증하여 삭제가 성공했음을 확인.
- guestBookRepository.findAll(): 데이터베이스에 있는 모든 방명록 글을 조회하여 articles 리스트에 저장
- assertThat(articles).isEmpty();: 조회한 articles 리스트가 비어 있는지를 확인. 이는 방명록 글이 성공적으로 삭제되었음을 나타냄.
각 기능별 테스트 코드 설명
6.방명록 글 수정 테스트
@DisplayName("updateArticle: 블로그 글 수정 성공")
@Test
public void updateArticle() throws Exception {
//given
final String url = "/api/guestbook/{id}";
final String author = "author";
final String content = "content";
Article savedArticle = guestBookRepository.save(Article.builder()
.author(author)
.content(content)
.build());
final String newAuthor = "new Author";
final String newContent = "new Content";
UpdateArticleRequest request = new UpdateArticleRequest(newAuthor, newContent);
// when
ResultActions result = mockMvc.perform(put(url, savedArticle.getId())
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(objectMapper.writeValueAsString(request)));
//then
result.andExpect(status().isOk());
Article article = guestBookRepository.findById(savedArticle.getId()).get();
assertThat(article.getAuthor()).isEqualTo(newAuthor);
assertThat(article.getContent()).isEqualTo(newContent);
}
- Given: 수정할 방명록 글의 URL과 기존 작성자, 내용을 정의하고, 방명록 글을 데이터베이스에 저장. 이어서 새로운 작성자와 내용을 준비하기 위해 UpdateArticleRequest 객체 생성.
- API 엔드포인트 URL, 저자 및 내용 설정
- guestBookRepository.save(...): 방명록 글을 데이터베이스에 저장
- Article.builder(): 작성자와 내용을 설정한 후, 방명록 글 객체를 생성하고 저장
- UpdateArticleRequest request = new UpdateArticleRequest(newAuthor, newContent);: 새로운 작성자와 내용을 포함하는 요청 객체 생성
- When: PUT 요청을 보내며, URL에는 방명록 글의 ID를 삽입하고, JSON으로 직렬화한 요청 본문 포함.
- mockMvc.perform(put(url, savedArticle.getId()): 방명록 글 ID를 포함한 PUT 요청 보냄.
- contentType(MediaType.APPLICATION_JSON_VALUE): 요청의 콘텐츠 타입을 JSON으로 지정
- .content(objectMapper.writeValueAsString(request)));: 생성한 수정 요청 객체를 JSON 형태의 문자열로 변환하여 요청 본문에 포함시킴. 이는 ObjectMapper를 사용하여 수행
- Then: 응답의 상태 코드가 200 OK인지 확인하고, 데이터베이스에서 해당 방명록을 조회하여 작성자와 내용이 올바르게 수정되었는지 검증.
- result.andExpect(status().isOk()): 요청이 성공적으로 처리되어 응답 상태 코드가 200 OK인지 확인
- Article article = guestBookRepository.findById(savedArticle.getId()).get();: 방금 수정한 방명록 글을 데이터베이스에서 다시 조회
- assertThat(article.getAuthor()).isEqualTo(newAuthor);: 수정된 글의 작성자가 새로운 작성자와 일치하는지 확인
- assertThat(article.getContent()).isEqualTo(newContent);: 수정된 글의 내용이 새로운 내용과 일치하는지 검증
[참고]
위키백과
스프링 부트 3 백엔드 개발자 되기
https://sjh9708.tistory.com/238
https://sjh9708.tistory.com/195
https://katfun.tistory.com/195
다음 내용
[Java] Spring Boot: ORM, JPA, 하이버네이트
이전 내용 [Java] Spring Boot: 테스트 코드이전 내용 [Java] Spring Boot: 방명록 Rest API 구현(feat. MySQL 연동)이전 내용 [Java] Spring Boot: 코드, 요청&응답 과정 이해하기이전 내용 [Java] Spring Boot: Maven Repository
puppy-foot-it.tistory.com