단위테스트와 통합테스트의 이해와 차이

Daniel·2024년 8월 13일
1

Back-End

목록 보기
42/48

들어가며

단위 테스트, 통합 테스트 란 말을 많이 들어보셨을꺼라 생각됩니다.
그냥 메서드 하나 테스트 하면 단위 테스트 많아지거나 프로그램 전체를 테스트하면 통합 테스트 아니야? 라고도 생각하시는 분들도 계시겠네요 ㅎㅎㅎ
이 포스트에서 정리해보려 합니다.

단위 테스트란?

단위 테스트(Unit Test)는 애플리케이션의 개별적인 "단위"를 테스트하는 것입니다.
여기서 "단위"란 일반적으로 메서드나 함수 같은 작은 코드를 의미합니다.
단위 테스트의 목적은 이 작은 코드가 기대한 대로 동작하는지를 확인하는 것입니다.
이러한 테스트는 해당 코드를 둘러싼 외부 요소들, 예를 들어 데이터베이스나 네트워크와 같은 의존성을 차단하고, 오로지 해당 코드의 논리에만 집중하여 테스트를 수행합니다.

ResultActions와 MvcResult의 역할

Spring MVC에서 단위 테스트를 작성할 때, MockMvc를 사용하여 컨트롤러 레벨에서 테스트를 수행합니다. 이때 ResultActionsMvcResult는 테스트 결과를 확인하고 세부 정보를 얻는 데 중요한 역할을 합니다.

ResultActions

ResultActionsMockMvc를 사용하여 요청을 수행한 후 결과를 체이닝 방식으로 처리할 수 있게 해주는 객체입니다. 이 객체를 통해 응답의 상태 코드, 반환된 데이터, 예외 등을 확인할 수 있습니다.

@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/")));
}
  • Given: 새로운 Article 객체를 JSON 문자열로 준비합니다.
  • When: MockMvc를 사용하여 /api/articles 엔드포인트에 POST 요청을 보냅니다. 이 요청의 결과는 ResultActions 객체로 반환됩니다.
  • Then: ResultActionsandExpect 메서드를 사용하여 응답 상태 코드가 201 Created인지, Location 헤더가 존재하는지, 그리고 Location 헤더가 예상한 값을 포함하는지 확인합니다.

MvcResult

MvcResultResultActions에서 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());
}
  • Given: 테스트용 Article 객체를 설정하고, 서비스 레이어의 findArticleById 메서드를 모킹하여 해당 객체를 반환하도록 합니다.
  • When: MockMvc를 사용하여 /api/articles/1 엔드포인트에 GET 요청을 수행합니다. 이 요청의 결과는 MvcResult 객체로 반환됩니다.
  • Then: MvcResult를 사용하여 응답 본문을 확인하고, ObjectMapper를 사용하여 JSON 응답을 Article 객체로 변환한 후 해당 객체의 속성을 검증합니다

첫 번째 예제는 ResultActions를 사용하여 테스트 체이닝과 상태 코드를 검증하는 데 중점을 두고,
두 번째 예제는 MvcResult를 사용하여 응답 데이터를 구체적으로 검토하는 데 중점을 둡니다.

단위 테스트의 장단점

장점

  • 속도와 효율성: 단위 테스트는 매우 빠르게 실행되므로 개발 중에 자주 실행할 수 있습니다. 이를 통해 코드 수정 후 즉각적인 피드백을 받을 수 있습니다.
  • 격리와 정밀성: 각 단위가 다른 요소들로부터 격리되어 있기 때문에, 문제의 원인을 쉽게 추적할 수 있습니다.
  • 초기 단계에서의 버그 발견: 코드의 작은 부분에서 발생하는 문제를 빠르게 발견할 수 있어, 복잡한 문제로 발전하기 전에 해결할 수 있습니다.

단점

  • 한정된 범위: 단위 테스트는 개별적인 코드 조각만을 테스트하므로, 시스템 전체가 기대한 대로 동작하는지를 보장할 수는 없습니다.
  • 모킹(Mock) 복잡성: 단위 테스트에서 외부 의존성을 제거하기 위해 모킹을 사용해야 하는 경우가 많은데, 이 과정이 복잡해질 수 있으며, 현실 세계의 시나리오를 충분히 반영하지 못할 수 있습니다.
  • 잘못된 자신감: 단위 테스트가 모두 통과되더라도, 실제 환경에서 전체 시스템이 올바르게 동작할 것이라는 보장은 없습니다.

단위 테스트를 사용하는 경우

  • 핵심 비즈니스 로직 테스트: 비즈니스 로직을 처리하는 핵심 메서드나 함수의 정확성을 보장할 때.
  • 개발 초기 단계: 코드 작성 직후, 해당 코드의 기본적인 기능이 제대로 구현되었는지를 확인할 때.
  • 빈번한 코드 변경: 코드가 자주 변경되는 상황에서, 각 변경이 기존 기능에 영향을 주지 않는지 확인할 때.

통합 테스트란?

통합 테스트(Integration Test)는 시스템의 여러 구성 요소들이 함께 작동할 때, 이들이 올바르게 상호작용하는지를 테스트하는 것입니다.
이는 개별적인 단위가 아니라, 여러 단위들이 통합되어 하나의 시스템으로서 동작하는지를 확인하는 과정입니다.
실제 데이터베이스, 파일 시스템, 네트워크 서비스 등과의 상호작용을 포함하여, 더 현실적인 시나리오에서 시스템의 동작을 검증합니다.

ResultActions와 MvcResult의 역할

통합 테스트에서는 ResultActionsMvcResult가 더욱 중요한 역할을 합니다.
실제 HTTP 요청을 통해 시스템의 여러 부분이 어떻게 상호작용하는지를 테스트할 수 있으며, 이를 통해 얻어진 MvcResult를 활용하여 응답의 세부 정보를 확인합니다.
예를 들어, 데이터베이스에 저장된 데이터가 실제로 올바르게 반환되는지를 확인할 수 있습니다.

ResultActions

@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/")));
}
  • Given: 통합 테스트에서 사용할 실제 Article 데이터를 JSON 형식으로 준비합니다.
  • When: MockMvc를 사용하여 /api/articles 엔드포인트에 POST 요청을 보냅니다. 이 요청의 결과는 ResultActions 객체로 반환됩니다.
  • Then: ResultActions를 사용하여 응답 상태 코드가 201 Created인지 확인하고, Location 헤더가 존재하며 예상된 URL을 포함하는지 검증합니다.

MvcResult

@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());
}
  • Given: 테스트용 Article 객체를 데이터베이스에 미리 저장합니다.
  • When: MockMvc를 사용하여 /api/articles/{id} 엔드포인트에 GET 요청을 보냅니다. 이 요청의 결과는 MvcResult 객체로 반환됩니다.
  • Then: MvcResult를 사용하여 응답 본문을 확인하고, ObjectMapper를 사용하여 JSON 응답을 Article 객체로 변환한 후 데이터베이스에 저장된 값과 일치하는지 검증합니다.

첫 번째 예제는 ResultActions를 사용하여 실제 HTTP 요청이 시스템의 여러 부분에 미치는 영향을 확인하고, 응답의 상태 코드와 헤더를 검증하는 방법을 보여줍니다.
두 번째 예제는 MvcResult를 사용하여 실제 HTTP 요청 후 응답의 세부 정보를 확인하고, 데이터베이스에 저장된 데이터를 검증하거나 서비스 계층에서 반환된 값과 일치하는지 확인하는 방법을 보여줍니다.

통합 테스트의 장단점

장점

  • 현실성: 실제 데이터와 외부 시스템과의 상호작용을 테스트하기 때문에, 시스템이 실제 환경에서 어떻게 동작할지를 더 정확하게 파악할 수 있습니다.
  • 포괄적인 테스트: 여러 구성 요소가 결합되어 올바르게 동작하는지를 확인함으로써, 시스템의 전반적인 안정성을 보장할 수 있습니다.
  • 통합의 자신감: 통합 테스트를 통해 서로 다른 모듈 간의 통합 문제가 없는지 확인할 수 있어, 배포 전에 더 큰 자신감을 가질 수 있습니다.

단점

  • 느린 실행 속도: 실제 데이터베이스나 네트워크와의 상호작용이 포함되므로, 테스트 실행 속도가 느려질 수 있습니다.
  • 복잡한 설정: 통합 테스트를 실행하기 위해서는 데이터베이스 초기화, 서버 설정 등 복잡한 준비 작업이 필요할 수 있습니다.
  • 불안정한 테스트: 네트워크 지연, 외부 서비스의 가용성 등 외부 요인에 의해 테스트 결과가 달라질 수 있습니다.

통합 테스트를 사용하는 경우

  • 시스템 간 상호작용 테스트: 서로 다른 모듈이나 서비스 간의 통합을 테스트할 때.
  • 실제 환경에 가까운 테스트: 실제 데이터베이스나 외부 API와 상호작용하는 시나리오를 테스트할 때.
  • 배포 전 시스템 안정성 검증: 전체 시스템이 올바르게 동작하는지 확인하고, 배포 전에 모든 모듈이 기대대로 작동하는지 보장할 때.

차이점

  • 테스트 범위: 단위 테스트는 개별 컴포넌트의 동작을 테스트하는 데 반해, 통합 테스트는 여러 컴포넌트가 결합하여 하나의 시스템으로 동작하는지를 테스트합니다.
  • 실행 속도: 단위 테스트는 일반적으로 매우 빠르게 실행되지만, 통합 테스트는 느린 편입니다.
  • 의존성: 단위 테스트는 모킹을 사용하여 외부 의존성을 제거하는 반면, 통합 테스트는 실제 외부 시스템과의 상호작용을 포함합니다.
  • 테스트 복잡성: 단위 테스트는 비교적 간단한 반면, 통합 테스트는 설정이 복잡하고 외부 요인에 의해 테스트 결과가 영향을 받을 수 있습니다.

단위테스트와 통합테스트의 결합

단위 테스트와 통합 테스트는 상호 보완적인 관계에 있습니다.
단위 테스트를 통해 코드의 작은 부분들이 올바르게 동작하는지를 빠르게 확인하고, 통합 테스트를 통해 시스템 전체가 올바르게 작동하는지를 검증함으로써, 두 가지 테스트의 장점을 결합할 수 있습니다.
이를 통해 개발자는 개별 코드의 정확성과 시스템의 통합성을 모두 보장할 수 있습니다.

예시 테스트 전략:

  • 단위 테스트: 개발 초기 및 코드 변경 시 빠르고 자주 실행하여 기본적인 동작을 보장.
  • 통합 테스트: 주요 릴리즈 전에 실행하여 전체 시스템의 안정성을 확인.

note

조금은 흩어져있던 테스트의 지식들이 조금은 뭉쳐진 것 같다....!

profile
응애 나 애기 개발자

0개의 댓글