-
Testing Spring Boot AppTest/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 가 쓰이는 곳에서 상속