프로젝트/뉴스타

[Spring boot] Java 테스트 코드

cks._.hong 2024. 6. 17. 10:27

Java 테스트 코드 왜 궁금했을까❓

뉴스타 서비스에서는 테스트 코드를 작성하여 서비스의 코드 품질을 높이고 장애를 사전에 방지하고자 작성해보려고 한다. 또한, 코드 수정 시 빠르게 검증하여 작업의 생산성을 향상시키고자 한다.

 

1. 단위 테스트  / 통합 테스트

1.1. 단위 테스트 (Unit Test)

  • 하나의 기능 또는 함수를 기준으로 독립적으로 수행하는 가장 작은 단위의 테스트
  • 하나의 기능이 정상적으로 동작하는지를 테스트하는 것으로 "어떤 기능이 실행되면 어떤 결과가 나온다" 정도로 테스트한다.

1.2. 단위 테스트 장점

  1. 테스트에 대한 시간과 비용을 절감
  2. 새로운 기능을 추가하거나 수정 시 빠른 테스트 가능
  3. 리팩토링 시에 안정성 확보
  4. 코드에 대한 문서화

1.3. 통합 테스트 (Integration Test)

  • 하나의 기능 또는 함수를 통합하는 과정에서 상호 간의 호환성을 확인하기 수행하는 테스트
  • 어플리케이션의 여러 개의 모듈로 구성되어 있는데, 모듈 간의 상호 연계가 잘 되어 동작하는지 검증하는 것이다.
  • 클라이언트로부터 API를 호출하여 올바르게 동작하는지 정도로 테스트한다.

1.4. 통합 테스트 장점

  1. 컴포넌트 간의 상호작용이 올바르게 동작하는지 확인 가능
  2. API 및 서비스 유효성을 검사 가능

 

2. 프로젝트 적용

테스트는 given-when-then 패턴으로 작성하여 가독성을 높일 수 있다. 테스트 코드는 문서의 역할도 하기 때문에 가독성은 중요하다고 할 수 있다.
  • JUnit과 Mockito를 활용하여 통합 테스트를 진행하려고 한다.
@SpringBootTest
@AutoConfigureMockMvc(addFilters = true)
class MemberControllerTest {
  ...
  @Test
  void matchingMember() throws Exception {
    // given
    // when
    mockMvc.perform(get("/members")
            .header("X-User-Id", "feb91399-aaf3-40ef-856d-35e6ddf8befb")) // 사용자 헤더
        // then
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(200))
        .andExpect(jsonPath("$.statusName").value("OK"))
        .andExpect(jsonPath("$.message").value("OK"))
        .andExpect(jsonPath("$.data.pw").value("feb91399-aaf3-40ef-856d-35e6ddf8befb"))
        .andExpect(jsonPath("$.data.signDate").exists());
  }

  @Test
  void createMember() throws Exception {
    // given
    // when
    mockMvc.perform(post("/members")
            .contentType(MediaType.APPLICATION_JSON) // Content-Type 헤더
            .content("{\"categories\":[100, 200, 300]}"))
        // then
        .andExpect(status().isCreated())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(201))
        .andExpect(jsonPath("$.statusName").value("CREATED"))
        .andExpect(jsonPath("$.message").value("Created Success"))
        .andExpect(jsonPath("$.data.pw").exists())
        .andExpect(jsonPath("$.data.signDate").exists());
  }
}
  • 유저 조회와 유저 생성에 대한 통합 테스트를 진행하고 있다.
  • perform()을 통해 HTTP 요청을 실행하고 andExpect()를 통해 Response를 검증한다.
@SpringBootTest
@AutoConfigureMockMvc(addFilters = true)
class RecordControllerTest {
  ...
  @Test
  void getRecords() throws Exception {
    //given
    //when
    mockMvc.perform(get("/records")
            .header("X-User-Id", "feb91399-aaf3-40ef-856d-35e6ddf8befb"))
        //then
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(200))
        .andExpect(jsonPath("$.statusName").value("OK"))
        .andExpect(jsonPath("$.message").value("OK"));
  }

  @Test
  void getRecordLikes() throws Exception {
    //given
    //when
    mockMvc.perform(get("/records/likes")
            .header("X-User-Id", "feb91399-aaf3-40ef-856d-35e6ddf8befb"))
        //then
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(200))
        .andExpect(jsonPath("$.statusName").value("OK"))
        .andExpect(jsonPath("$.message").value("OK"));
  }

  @Test
  void createRecord() throws Exception {
    //given
    //when
    mockMvc.perform(post("/records")
            .header("X-User-Id", "feb91399-aaf3-40ef-856d-35e6ddf8befb")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"articleId\":1}"))
        //then
        .andExpect(status().isCreated())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(201))
        .andExpect(jsonPath("$.statusName").value("CREATED"))
        .andExpect(jsonPath("$.message").value("Created Success"));
  }

  @Test
  void updateRecordLikes() throws Exception {
    //given
    //when
    mockMvc.perform(patch("/records")
            .header("X-User-Id", "feb91399-aaf3-40ef-856d-35e6ddf8befb")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"articleId\":1 , \"likes\":true }"))

        //then
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json"))
        .andExpect(jsonPath("$.statusCode").value(200))
        .andExpect(jsonPath("$.statusName").value("OK"))
        .andExpect(jsonPath("$.message").value("OK"));
  }
}
  • 뉴스 시청 기록 API에 대해 통합 테스트 코드를 작성해보았다.
  • 이 외에도 위와 같은 형식으로 API 테스트를 진행했다.