티스토리 뷰
spring boot REST API Web 프로젝트 (11 - 1) - JUnit Test (단위 테스트)
구름뭉치 2021. 8. 23. 15:59스프링 부트 REST API WEB 프로젝트
깃헙 링크
https://github.com/choiwoonsik/springboot_RestApi_App_Project/tree/main/restApiSpringBootApp
수행 목록
- 환경구성 및 helloworld 출력
- H2 DB 연동
- Swagger API 문서 연동
- REST API 설계
- RestControllerAdvice를 이용한 통합 예외 처리
- Entity - DTO 분리
- MessageSource를 이용해 예외 메시지 다국화
- JPA Aduting을 이용해 객체 생성시간/수정시간 적용
- 스프링 시큐리티 + Jwt를 이용해서 인증 및 권한 체크
- 스프링 시큐리티 AuthenticationEntryPoint, AccessDenied로 인증 및 인가 예외처리
- Jwt AccessToken + RefreshToken으로 보안성과 사용자 편의성 고도화하기
- JUnit Test (단위 테스트)
- JUnit Test (통합 테스트)
- OAuth 2.0 정리
- OAuth 2.0 카카오 로그인 part.1 Authorization code + Token 발급
- OAuth 2.0 카카오 로그인 part.2 토큰으로 회원 가입 / 로그인
- OAuth 2.0 카카오 로그인 테스트 검증
- 환경별 설정을 위해서profile 분리하기
정상적인 구현을 확인하기 위한 JUnit 테스트를 진행해보자
의존성 추가
// 스프링 테스트
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
테스트는 두 종류로 나눠볼 수 있다.
- 통합 테스트
- @SpringBootTest
- 전체적인 플로우를 검증하기 위한 테스트이다.
- 애플리케이션의 설정, 모든 Bean을 전부 로드해서 테스트 하므로 운영환경과 가장 유사한 테스트가 가능하다.
- 단점
- 그만큼 많은 Bean을 로드하므로 시간도 오래걸리고 무겁다.
- 테스트 단위가 크므로 디버깅이 어렵다. 어디가 잘못됐는지 찾기가 힘듬
- 단위 테스트
- @DataJpaTest
- @WebMvcTest
- @RestClientTest
- @JsonTest
- 단위 테스트는 각 단위 테스트 성격에 맞게 필요한 컴포넌트만 확인해서 Bean으로 등록한다.
- 기능 검증 위주의 테스트이다.
- 따라서 더욱 가볍고 빠른 테스트가 가능하다.
- 단점
- 전체적인 로직을 테스트가 힘들다.
단위 테스트에 대해서 알아보자
1. JPA 테스트를 위한 @DataJpaTest
현재 User Entity는 Spring Data JPA 를 상속받아서 구현중에 있다. @DataJpaTest 애노테이션을 사용하면 이러한 JPA를 손쉽게 테스트할 수 있는 환경을 만들어준다.
- @DataJpaTest를 달아주면 다른 컴포넌트는 로드하지 않고 @Entity를 읽어서 스프링 데이터 JPA Repository내용을 테스트할 수 있는 환경을 만들어 준다.
- @Transactional을 포함하고 있으므로 테스트가 완료되면 따로 롤백을 해주지 않아도 된다.
- JPA 관련된 설정만 불러온다. (즉, Entity 관련 테스트를 할 때 용이)
- 설정이 정상적인지, JPA를 사용해서 데이터를 올바르게 등록 / 수정 / 삭제 / 조회 하는지의 테스트가 가능하다
- 기본적으로 in-memory embedded database에 대한 테스트를 진행한다
- @AutoConfigureTestDatabase(reploace = AutoConfigureTestDatabase.Replace.NONE) 애노테이션을 달아주면 메모리 DB가 아니라 실제 DB에서 테스트가 가능하다.
- @ActiveProfiles("test")등의 프로파일 설정도 가능하다
결론적으로, JPA Repo의 단위 테스트가 필요하다면 @DataJpaTest를 사용하는게 효과적이다.
회원 생성 후 검증 코드예시
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserJpaRepoTest {
@Autowired
private UserJpaRepo userJpaRepo;
@Autowired
private PasswordEncoder passwordEncoder;
private String name = "woonsik";
private String email = "dnstlr2933@naver.com";
private String password = "myPassWord";
@Test
public void 회원저장_후_이메일로_회원검색() throws Exception {
//given
userJpaRepo.save(User.builder()
.name(name)
.email(email)
.password(passwordEncoder.encode(password))
.nickName(name)
.roles(Collections.singletonList("ROLE_USER"))
.build());
//when
User user = userJpaRepo.findByEmail(email).orElseThrow(CUserNotFoundException::new);
//then
assertNotNull(user);
assertEquals(user.getUsername(), user.getUsername());
assertThat(user.getName()).isEqualTo(name);
assertThat(user.getNickName()).isEqualTo(name);
}
}
JPA를 사용하지 않는다면 @JdbcTest를 사용하면 된다.
2. MVC (컨트롤러) 테스트를 위한 @WebMvcTest
컨트롤러가 예상대로 동작하는지를 위한 테스트이다.
아래 내용만 스캔해서 로딩한다
@Controller, @ControllerAdvice, @JsonComponent,
Converter, GenericConverter, Filter, HandlerInterceptor, WebMvcConfigurer, HandlerMethodArgumentResolver
---
@Service, @Component, @Repository는 스캔하지 않는다
- MockBean, MovkMVC를 자동 구성하여 테스트 가능하도록 한다.
- 스프링 시큐리티에 대한 테스트도 지원한다.
- 테스트할 특정 컨트롤러 클래스를 명시하도록 한다.
Controller
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
테스트 코드
@RunWith(SpringRunner.class)
@WebMvcTest
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void testHello() throws Exception{
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
}
3. REST 클라이언트 테스트를 위한 @RestClientTest
@RestClientTest를 사용하면 REST 클라이언트 테스트가 가능하다.
- REST API 통신이 JSON 형식의 데이터를 예상대로 반환하는지 테스트하게 된다.
예를들어, Apache HttpClient or Spring RestTemplate을 사용하여 외부 서버에 웹 요청을 보내는 경우에 이에 응답할 Mock서버를 만들어주는 것이라고 생각하면 된다.
테스트 코드
@RunWith(SpringRunner.class)
@RestClientTest(RestUserService.class)
public class UserServiceMockTest {
@Autowired
private RestUserService userService;
@Autowired
private MockRestServiceServer server;
@Test
public void 회원가져오기() throws Exception {
//given
server
.expect(MockRestRequestMatchers
.requestTo("/v1/user/email/dnstlr2933@naver.com"))
.andRespond(MockRestResponseCreators
.withSuccess(
new ClassPathResource("/test.json", getClass()),
MediaType.APPLICATION_JSON)
);
//when
User byEmail = userService.getUserByEmail("dnstlr2933@naver.com");
//then
Assertions.assertThat(byEmail.getEmail()).isEqualTo("dnstlr2933@naver.com");
Assertions.assertThat(byEmail.getName()).isEqualTo("woonsik");
server.verify();
}
}
컨트롤러에서 처리하는 URL로 요청을 보내고 성공시 test.json파일에 정의한 값으로 응답을 준다. 응답값과 정의한 값을 비교
test.json 파일
{
"email": "dnstlr2933@naver.com",
"name": "woonsik"
}
RestUserService 클래스
@Service
public class RestUserService {
private final RestTemplate restTemplate;
public RestUserService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public User getUserByEmail(String email) {
return restTemplate.getForObject("/v1/user/email/{email}", User.class, email);
}
}
❗️해당 테스트를 할 때 주의사항❗️
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'jpaMappingContext':
Invocation of init method failed; nested exception is java.lang.IllegalArgumentException:
JPA metamodel must not be empty!
@WebMvcTest로 테스트를 진행하던 도중 위와 같은 에러가 발생했는데, 결론은 JPA metamodel must not be empty! 로, JPA 메타 모델은 비워둘 수 없다고 알려주는 것이다.
@WebMvcTest는 JPA 생성과 관련된 기능이 전혀 존재하지 않는 테스트 어노테이션이다.
그런데 어떤 테스트를 진행하던간에 booststrapping클래스는 항상 로딩되므로 @EnableJpaAuditing이 붙어있는 bootstrpping클래스를 로딩하게 된다.
현재 JPA Auditing 기능을 사용하고 있으므로 @SpringBootApplication에 @EnableJPaAuditing을 추가하여 사용하고 있어서 로딩하게 되는데 JPA 관련 빈은 로딩하지 않으니깐 에러가 발생하게 되는 것이다.
@EnableJpaAuditing
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
해결방법
간단하게 해결이 가능하다.
@EnableJpaAditing을 분리해주면 된다
@EnableJpaAuditing
@Configuration
public class JpaAuditingConfiguration {
}
'스프링 > 스프링부트 RestAPI 프로젝트' 카테고리의 다른 글
- Total
- Today
- Yesterday