단위 테스트, 통합 테스트 란 말을 많이 들어보셨을꺼라 생각됩니다.
그냥 메서드 하나 테스트 하면 단위 테스트 많아지거나 프로그램 전체를 테스트하면 통합 테스트 아니야? 라고도 생각하시는 분들도 계시겠네요 ㅎㅎㅎ
이 포스트에서 정리해보려 합니다.
단위 테스트(Unit Test)는 애플리케이션의 개별적인 "단위"를 테스트하는 것입니다.
여기서 "단위"란 일반적으로 메서드나 함수 같은 작은 코드를 의미합니다.
단위 테스트의 목적은 이 작은 코드가 기대한 대로 동작하는지를 확인하는 것입니다.
이러한 테스트는 해당 코드를 둘러싼 외부 요소들, 예를 들어 데이터베이스나 네트워크와 같은 의존성을 차단하고, 오로지 해당 코드의 논리에만 집중하여 테스트를 수행합니다.
Spring MVC에서 단위 테스트를 작성할 때, MockMvc
를 사용하여 컨트롤러 레벨에서 테스트를 수행합니다. 이때 ResultActions
와 MvcResult
는 테스트 결과를 확인하고 세부 정보를 얻는 데 중요한 역할을 합니다.
ResultActions
는 MockMvc
를 사용하여 요청을 수행한 후 결과를 체이닝 방식으로 처리할 수 있게 해주는 객체입니다. 이 객체를 통해 응답의 상태 코드, 반환된 데이터, 예외 등을 확인할 수 있습니다.
@Test
public void testCreateArticle() throws Exception {
// Given: 새로 생성할 Article 데이터 준비
String newArticleJson = "{\"title\": \"New Article\", \"content\": \"This is a new article.\"}";
// When: MockMvc를 사용하여 POST 요청 수행
ResultActions resultActions = mockMvc.perform(post("/api/articles")
.contentType(MediaType.APPLICATION_JSON)
.content(newArticleJson))
.andExpect(status().isCreated());
// Then: 응답에서 상태 코드와 특정 헤더 값을 확인
resultActions.andExpect(header().exists("Location"))
.andExpect(header().string("Location", containsString("/api/articles/")));
}
Article
객체를 JSON 문자열로 준비합니다.MockMvc
를 사용하여 /api/articles
엔드포인트에 POST 요청을 보냅니다. 이 요청의 결과는 ResultActions
객체로 반환됩니다.ResultActions
의 andExpect
메서드를 사용하여 응답 상태 코드가 201 Created
인지, Location
헤더가 존재하는지, 그리고 Location
헤더가 예상한 값을 포함하는지 확인합니다.MvcResult
는 ResultActions
에서 andReturn()
메서드를 호출하여 얻을 수 있는 객체로, 실제 HTTP 응답의 상세 정보를 포함합니다. 여기에는 상태 코드, 응답 본문, 헤더, 요청 처리에 사용된 컨트롤러 등의 정보가 포함됩니다. 이를 통해 테스트가 좀 더 세밀하게 수행될 수 있습니다.
@Test
public void testGetArticleDetails() throws Exception {
// Given: 테스트할 Article 객체를 생성하고, 모킹된 서비스 메서드가 해당 객체를 반환하도록 설정
Article article = new Article(1L, "Test Title", "Test Content");
when(articleService.findArticleById(1L)).thenReturn(article);
// When: MockMvc를 사용하여 GET 요청 수행 후 MvcResult 객체를 반환받음
MvcResult mvcResult = mockMvc.perform(get("/api/articles/1"))
.andExpect(status().isOk())
.andReturn();
// Then: MvcResult를 통해 응답 본문을 확인하고 Article 객체로 변환하여 검증
String responseContent = mvcResult.getResponse().getContentAsString();
Article resultArticle = objectMapper.readValue(responseContent, Article.class);
assertEquals("Test Title", resultArticle.getTitle());
assertEquals("Test Content", resultArticle.getContent());
}
Article
객체를 설정하고, 서비스 레이어의 findArticleById
메서드를 모킹하여 해당 객체를 반환하도록 합니다.MockMvc
를 사용하여 /api/articles/1
엔드포인트에 GET 요청을 수행합니다. 이 요청의 결과는 MvcResult
객체로 반환됩니다.MvcResult
를 사용하여 응답 본문을 확인하고, ObjectMapper
를 사용하여 JSON 응답을 Article
객체로 변환한 후 해당 객체의 속성을 검증합니다첫 번째 예제는 ResultActions
를 사용하여 테스트 체이닝과 상태 코드를 검증하는 데 중점을 두고,
두 번째 예제는 MvcResult
를 사용하여 응답 데이터를 구체적으로 검토하는 데 중점을 둡니다.
통합 테스트(Integration Test)는 시스템의 여러 구성 요소들이 함께 작동할 때, 이들이 올바르게 상호작용하는지를 테스트하는 것입니다.
이는 개별적인 단위가 아니라, 여러 단위들이 통합되어 하나의 시스템으로서 동작하는지를 확인하는 과정입니다.
실제 데이터베이스, 파일 시스템, 네트워크 서비스 등과의 상호작용을 포함하여, 더 현실적인 시나리오에서 시스템의 동작을 검증합니다.
통합 테스트에서는 ResultActions
와 MvcResult
가 더욱 중요한 역할을 합니다.
실제 HTTP 요청을 통해 시스템의 여러 부분이 어떻게 상호작용하는지를 테스트할 수 있으며, 이를 통해 얻어진 MvcResult
를 활용하여 응답의 세부 정보를 확인합니다.
예를 들어, 데이터베이스에 저장된 데이터가 실제로 올바르게 반환되는지를 확인할 수 있습니다.
@Test
public void testCreateArticleIntegration() throws Exception {
// Given: 새로 생성할 Article 데이터 준비
String newArticleJson = "{\"title\": \"Integration Test Article\", \"content\": \"Content for integration test.\"}";
// When: MockMvc를 사용하여 POST 요청을 통해 실제로 Article 생성
ResultActions resultActions = mockMvc.perform(post("/api/articles")
.contentType(MediaType.APPLICATION_JSON)
.content(newArticleJson))
.andExpect(status().isCreated());
// Then: 응답 상태 코드와 Location 헤더를 확인
resultActions.andExpect(header().exists("Location"))
.andExpect(header().string("Location", containsString("/api/articles/")));
}
Article
데이터를 JSON 형식으로 준비합니다.MockMvc
를 사용하여 /api/articles
엔드포인트에 POST 요청을 보냅니다. 이 요청의 결과는 ResultActions
객체로 반환됩니다.ResultActions
를 사용하여 응답 상태 코드가 201 Created
인지 확인하고, Location
헤더가 존재하며 예상된 URL을 포함하는지 검증합니다.@Test
public void testGetArticleIntegration() throws Exception {
// Given: 테스트용 Article을 데이터베이스에 미리 저장
Article savedArticle = new Article(1L, "Integration Test Title", "Integration Test Content");
articleRepository.save(savedArticle);
// When: MockMvc를 사용하여 GET 요청을 통해 Article을 조회
MvcResult mvcResult = mockMvc.perform(get("/api/articles/" + savedArticle.getId()))
.andExpect(status().isOk())
.andReturn();
// Then: MvcResult를 통해 응답 본문을 확인하고 Article 객체로 변환하여 검증
String responseContent = mvcResult.getResponse().getContentAsString();
Article resultArticle = objectMapper.readValue(responseContent, Article.class);
assertEquals(savedArticle.getTitle(), resultArticle.getTitle());
assertEquals(savedArticle.getContent(), resultArticle.getContent());
}
Article
객체를 데이터베이스에 미리 저장합니다.MockMvc
를 사용하여 /api/articles/{id}
엔드포인트에 GET 요청을 보냅니다. 이 요청의 결과는 MvcResult
객체로 반환됩니다.MvcResult
를 사용하여 응답 본문을 확인하고, ObjectMapper
를 사용하여 JSON 응답을 Article
객체로 변환한 후 데이터베이스에 저장된 값과 일치하는지 검증합니다.첫 번째 예제는 ResultActions
를 사용하여 실제 HTTP 요청이 시스템의 여러 부분에 미치는 영향을 확인하고, 응답의 상태 코드와 헤더를 검증하는 방법을 보여줍니다.
두 번째 예제는 MvcResult
를 사용하여 실제 HTTP 요청 후 응답의 세부 정보를 확인하고, 데이터베이스에 저장된 데이터를 검증하거나 서비스 계층에서 반환된 값과 일치하는지 확인하는 방법을 보여줍니다.
단위 테스트와 통합 테스트는 상호 보완적인 관계에 있습니다.
단위 테스트를 통해 코드의 작은 부분들이 올바르게 동작하는지를 빠르게 확인하고, 통합 테스트를 통해 시스템 전체가 올바르게 작동하는지를 검증함으로써, 두 가지 테스트의 장점을 결합할 수 있습니다.
이를 통해 개발자는 개별 코드의 정확성과 시스템의 통합성을 모두 보장할 수 있습니다.
예시 테스트 전략:
조금은 흩어져있던 테스트의 지식들이 조금은 뭉쳐진 것 같다....!