ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Testing Spring Boot App
    Test/Testing Spring Boot App 2023. 10. 27. 17:10

    Repository Layer

    @DataJpaTest

    - 저장소 계층만 테스트하기  위해 사용

    - 내장된 메모리에 자동으로 구성

    - Spring Data JPA 리포지토리 및 JPA 관련 테스트할 수 있다.


    Service Layer

    @ExtendWith(MockitoExtension.class)

    - JUnit 5와 Mockito를 통합하여 테스트를 작성하고 실행할 수 있도록 도와준다.

     

    @Mock

    - 테스트 대상 클래스에서 다른 클래스 또는 의존성 객체를 대신하는 모의 객체를 생성한다.

    - 즉, 테스트 대상 클래스 내에서 특정 메서드를 호출하거나 특정 객체를 사용할 때, 이를 실제 객체 대신 모의 객체가 대신 동작한다.

     

    @InjectMocks

    - 테스트 대상 클래스에 @Mock 으로 생성된 모의 객체를 주입한다.

    - 즉, @InjectMocks를 붙인 클래스 내부에서 @Mock로 생성된 모의 객체들을 사용하며 테스트 대상 클래스의 행위를 검증한다.

     

    BDDMockito.given

    - Mock 객체에게 메서드 호출에 대한 기대값을 정의할 수 있다.

    - 이렇게 정의된 기대값에 따라 Mock 객체가 메서드를 호출하면 특정한 반환값을 제공하거나 예외를 던질 수 있다. 이를 통해 단위 테스트 중에 외부 의존성을 컨트롤하고 특정 시나리오를 시뮬레이션할 수 있다.
    - void를 반환하는 메서드를 스텁할 때 : willDoNothing() 사용

    willDoNothing().given(employeeRepository).deleteById(1L);

     

    BDDMockito.verify

    verify(employeeRepository, never()).save(any(Employee.class));

    - employeeRepository의 save 메서드가 호출되지 않았고, save 메서드가 어떤 종류의 Employee 객체를 인수로 받더라도 무시하겠다.

    - 즉, save 메서드 호출 여부를 확인하면서 인수에 관한 구체적인 내용을 무시하고 검증하는 방법


    Controller Layer

    @WebMvcTest

    - 스프링 부트는 스프링 컨트롤러를 테스트하기 위해 해당 어노테이션을 제공

    - 기본적으로 애플리케이션 컨텍스트를 지우고 필요한 빈만 로드한다.

    - 필요한 구성요소만 로드하므로 JUnit 테스크 케이스가 훨씬 빨라진다.

     

    @SpringBootTest

    - 통합테스트, 컨트롤 레이어에서 데이터베이스까의 전체 흐름을 테스트할 때 사용

    - 전체 애플리케이션 컨텍스트를 로드한다.

     

    예시)

    @WebMvcTest
    class EmployeeControllerTest {
        @Autowired
        private MockMvc mockMvc;
        @Autowired
        private ObjectMapper objectMapper;
    
        @MockBean
        private EmployeeService employeeService;
    
        @Test
        @DisplayName("컨트롤러 테스트")
        void givenEmployeeObject_whenCreateEmployee_thenReturnSavedEmployee() throws Exception {
            // given
            Employee employee = Employee.builder()
                    .firstName("p")
                    .lastName("hm")
                    .email("phm@naver.com")
                    .build();
            BDDMockito.given(employeeService.saveEmployee(ArgumentMatchers.any(Employee.class)))
                    .willAnswer((invocation) -> invocation.getArgument(0));
    
            // when
            ResultActions response = mockMvc.perform(MockMvcRequestBuilders.post("/api/employees")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(employee)));
    
            // then
            response.andDo(MockMvcResultHandlers.print())
                    .andExpect(MockMvcResultMatchers.status().isCreated())
                    .andExpect(MockMvcResultMatchers.jsonPath("$.firstName", CoreMatchers.is(employee.getFirstName())))
                    .andExpect(MockMvcResultMatchers.jsonPath("$.lastName", CoreMatchers.is(employee.getLastName())))
                    .andExpect(MockMvcResultMatchers.jsonPath("$.email", CoreMatchers.is(employee.getEmail())));
    
        }
    }

    - given 부분 : saveEmployee가 호출 될 때 전달된 Employee 객체를 반환하기 위함.

    - MockMvcRequestBuilders / MockMvcResultHandlers / MockMvcResultMatchers / CoreMatchers

    - $ 는 JSON 개체의 배열을 의미한다.


    Integratioin Testing

    @SpringBootTest

    - 전체 애플리케이션 컨텍스트의 로드, 모든 빈을 로드

    - 내장 서버를 시작하고 모든 항목을 로드한다.

     

    Repository Integration

    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

    - 레파지토리 단위 테스트는 H2 인메모리 데이터베이스를 쓴다. 해당 설정을 추가하면 H2를 비활성화되고 기본적으로 연결된 데이터베이스를 사용한다.

     


    Integration Testing using Testcontainers

    - Test Containers 는 경량성을 제공하는 JUnit 테스트를 지원하는 Java 라이브러리이다.

    - 기본적으로 테스트 컨테이너를 사용하면 JUnit 테스트 케이스 자체에서 Docker 컨테이너를 사용할 수 있다.

    - 즉, 테스트 컨테이너를 사용하면 외부 컨테이너를 설치할 필요가 없다는 것을 의미한다.

     

    @Testcontainers

    - JUnit5와 Testcontainers의 통합을 제공한다는 것을 의미

    - @Container 를 선택하고 컨테이너 수명 주기 메서드를 호출

     

    @Container

    @Container
    private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer();

    - MySQLContainer 클래스는 기본적으로 Docker 허브에서 Docker 이미지를 다운로드한다.

    - 컨테이너 인스턴스를 생성할 때마다 정적으로 정의해야 테스트 메서드마다 사용가능!

     

    싱글톤 컨테이너 패턴

    public abstract class AbstractionBaseTest {
    
        static final MySQLContainer MY_SQL_CONTAINER;
    
        static {
            MY_SQL_CONTAINER = new MySQLContainer("mysql:lastest")
                    .withUsername("username")
                    .withPassword("password") 
                    .withDatabaseName("ems");
    
            MY_SQL_CONTAINER.start();
        }
    
        @DynamicPropertySource  // 애플리케이션 컨텍스트에 등록
        public static void dynamicPropertySource(DynamicPropertyRegistry registry) {
            registry.add("spring.datasource.url", MY_SQL_CONTAINER::getJdbcUrl);
            registry.add("spring.datasource.username", MY_SQL_CONTAINER::getUsername);
            registry.add("spring.datasource.password", MY_SQL_CONTAINER::getPassword);
        }
    }

    - @Testcontainers / @Container 삭제 가능

    - 해당 MySQLContainer 가 쓰이는 곳에서 상속

     

    댓글

Designed by Tistory.