본 포스팅은 스스로 공부한 내용을 정리하고 기록하기 위하여 올리는 내용이며, 잘못된 내용이 있을 수도 있음을 미리 밝힙니다. 잘못된 내용이 있거나, 더 좋은 방법이 있다면 댓글로 남겨주시기 바랍니다.
현재까지 프로젝트를 진행하면서 귀찮다는 이유로 test code 의 작성을 미뤄왔고, 항상 다음과 같은 순서로 완성된 Restful api 를 검증하였습니다.
- 코드 작성
- 애플리케이션 실행
- PostMan 으로 Restful API 요청
- log 나 Print 로 결과확인
- 애플리케이션 종료
- 코드 수정
하지만 프로젝트가 점점 진행되면서 다음과 같은 순서를 지속하기가 어려워졌고, Test code 를 작성하기로 결정했습니다.
Test code 도입후 테스트 과정은 다음과 같습니다.
- 코드를 수정
- 테스트 코드 실행
- 결과 확인
이처럼 테스트 코드는 만들어진 API 를 검증하는데 시간절약을 할수있습니다.
테스트 코드를 만들때 데이터를 미리 기입하고, 테스트가 끝날때의 정리하는 행동등을 만들어줄수있습니다.
testImplementation('org.springframework.boot:spring-boot-starter-test')
@SpringBootTest 를 이용하여 전체 애플리케이션의 컨텍스트를 생성하도록 요청 할수있습니다. 모든 빈들을 스캔하고 애플리케이션 컨텍스트 생성하므로 손쉽게 통합 테스트를 위한 환경을 준비해줄수있습니다.
@SpringBootTest는 기본적으로 모든 빈을 탐색하고 등록합니다. 따라서 특정 계층만 테스트가 필요한 상황에서 @SpringBootTest를 사용하면 불필요하게 무거워지고 시간이 오래 걸리게 됩니다. 그래서 스프링은 특정 부분만 테스트 할수 있도록 sliceTest를 위한 어노테이션들을 제공합니다. slice Test는 unit Test 가 아니라는 점에 주의해야 합니다.
다음은 @SpringBootTest 를 활용하여 service 레이어를 test 한 코드입니다.
@SpringBootTest
@ActiveProfiles("test")
@Transactional
@SqlGroup({
@Sql(value = "/sql/user-service-test-data.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD),
})
public class ImageServiceTest {
@Autowired
private ImageService imageService;
@MockBean
private S3Util s3Util;
@Mock
private MultipartFile multipartFile;
@Test
void uploadImage_로이미지를업로드할수있다() throws IOException {
//given
when(multipartFile.getContentType()).thenReturn("image/jpeg");
when(s3Util.uploadFile(multipartFile)).thenReturn("image123.jpg");
when(s3Util.getImageUrl("image123.jpg")).thenReturn("https://example.com/image123.jpg");
//when
String imageUrl = imageService.uploadImage(multipartFile);
//then
Assertions.assertThat(imageUrl).isNotEmpty();
}
}
@WebMvcTest를 사용하여 컨텍스트의 웹 계층만 생성하도록 요청할수 있습니다. @WebMvcTest는 애플리케이션 컨텍스트를 만들 때 컨트롤러와 연관된 빈들만을 제한적으로 찾아서 등록합니다. 그러므로 일반적인 @Component나 @ConfigurationProperties 빈들은 스캔되지 않습니다.
@WebMvcTest 내부에는 @AutoConfigureMockMvc가 들어있습니다. 그러므로 @Autowired 로 MockMvc를 주입받을 수 있습니다.
( @AutoConfigureMockMvc 를 이용하여 편리한 빌드 클래스 MockMvc를 통해 HTTP 요청을 DispatcherServlet으로 보낼수있습니다.)
@WebMvcTest(UserController.class)
@ActiveProfiles(profiles = "test")
@Import(SecurityConfig.class)
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private UserService userService;
@MockBean
private BCryptPasswordEncoder bCryptPasswordEncoder;
@MockBean
private JwtTokenProvider jwtTokenProvider;
@MockBean
private KafkaProducerService kafkaProducerService;
private final ObjectMapper objectMapper = new ObjectMapper();
@Test
public void 회원가입() throws Exception {
//given
String email = "fddfdassa@naver.com";
String password = "1234sda";
String nickName = "suhoon";
CreateUserRequest createUserRequest = new CreateUserRequest(email, nickName, password, null);
String body = objectMapper.writeValueAsString(createUserRequest);
BDDMockito.doNothing().when(kafkaProducerService).sendMessage(any());
//when
mvc.perform(
post("/sign-up")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().is(200));
//then
}
}
@DataJpaTest 는 JPA 레포지토리 테스트를 하기위해 만들어졌습니다. 기본적으로 @Entity 가 있는 엔티티 클래스들을 스캔하며 테스트를 위한 TestEntityManger를 사용해 JPA 레포지토리들을 설정해줍니다. @DataJpaTest에는 @Transactional 어노테이션이 들어가있어서 기본적으로 모든 테스트가 롤백됩니다. 롤백을 원하지 않는다면 @Rollback(false)를 추가하도록 합니다.
@DataJpaTest 안에는 @AutoConfigureTestDataBase가 내장되어있습니다.
@AutoConfigureTestDatabase 어노테이션의 기본 설정값인 Replace.Any를 사용하면 기본적으로 내장된 임베디드 데이터베이스를 사용한다. 따라서 Test를 내장 임베디드 데이터베이스가 아닌 실제 데이터베이스를 사용해서 할것이라면, @AutoConfigureTestDatabase(replace = Replace.NONE)
이 옵션을 꼭 넣어주어야 합니다.
@DataJpaTest(showSql = true)
@AutoConfigureTestDatabase(replace = Replace.NONE)
@ActiveProfiles("test")
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void db가_잘연결되어있다() {
//given
UserEntity userEntity = UserEntity.builder()
.id(10L)
.email("suhoon@naver.com")
.build();
//when
UserEntity user = userRepository.save(userEntity);
//then
Assertions.assertThat(user.getEmail()).isEqualTo("suhoon@naver.com");
}
}
SpringBoot TEST DOCS
TEST code 작성예시
SpringBoot TEST 어노테이션
관련 유튜브 영상
스프링 부트 통합테스트 방법과 팁