첫 스프링 프로젝트를 혼자서 맡아 진행하면서, 디프만 스터디에서 배운 테스트코드 작성 방법을 적용해 보았습니다.
체험(Experience)를 생성할 때, 로그인한 사용자의 정보를 가져와 작성자 외래키로 저장해야 했습니다.
Spring Security + OAuth2를 활용하여 구글 소셜 로그인을 구현하였기 때문에
로그인한 사용자의 정보는 Spring Security가 관리하고 있었습니다.
체험을 생성하는 Controller, Service 코드는 다음과 같았습니다.
// Controller
@PostMapping("")
public ResponseEntity<Experience> create(
@CookieValue("token") String token,
@RequestBody ExperienceCreate experienceCreate) {
// 쿠키 토큰이 없으면 redirect
if (token == null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Location", "/");
return new ResponseEntity<Experience>(headers, HttpStatus.FOUND);
}
// body로 받은 experienceCreate로 체험 생성
Experience experience = experienceService.create(experienceCreate);
return ResponseEntity.ok().body(experience);
}
// Service
@Transactional
public Experience create(ExperienceCreate experienceCreate) {
// SecurityContextHolder에서 현재 로그인한 사용자 정보를 가져옵니다.
Authentication authentication
= SecurityContextHolder
.getContext()
.getAuthentication();
// CustomOAuth2User 객체로 저장되어 있으므로 변환해줍니다.
CustomOAuth2User userDetails
= (CustomOAuth2User) authentication.getPrincipal();
// 작성자 객체를 DB에서 조회합니다.
User writer = userRepository.getById(userDetails.getId());
// 체험을 생성합니다.
Experience experience = Experience.from(writer, experienceCreate);
experience = experienceRepository.save(experience);
return experience;
}
Service의 create 메서드를 테스트하려고 보니, 난감했습니다.
SecurityContextHolder를 사용하려면 테스트코드에서 SpringBoot를 띄워야 했으니까요!
원래는 빨간 박스 형태였는데, 이러한 의존성은 테스트코드를 무겁게 만드므로 authorizationUtil 인터페이스를 추가하였습니다.
이 인터페이스를 구현하는 authorizationUtilImpl을 만들고, 여기서 SecurityContextHolder를 사용하게 설계를 변경하였습니다.
이제 테스트코드에서는 FakeAuthorizationUtil을 사용하여
SecurityContextHolder 없이 임의의 사용자 id를 고정적으로 넘겨주도록 구현했습니다.
void 체험생성() {
// given
User user = User.builder()
.id(userId)
.email("abc@gmail.com")
.build();
userRepository.save(user);
ExperienceCreate experience = ExperienceCreate.builder()
.title("title")
.location("location")
.datetime(LocalDateTime.of(2024, 6, 25, 16, 41, 0))
.content("content")
.duration(LocalTime.of(4, 0, 0))
.cost(10000)
.contact("contact")
.limitMember(15)
.language("language")
.build();
// when
Experience newExperience = experienceService.create(experience);
// then
Assertions.assertThat(newExperience.getTitle()).isEqualTo("title");
Assertions.assertThat(newExperience.getLocation()).isEqualTo("location");
Assertions.assertThat(newExperience.getWriter().getEmail()).isEqualTo("abc@gmail.com");
}
이런 체험 생성 테스트코드를 작성할 때
class ExperienceServiceTest {
ExperienceService experienceService;
ExperienceRepository experienceRepository = new FakeExperienceRepository();
UserRepository userRepository = new FakeUserRepository();
long userId = 1L;
@BeforeEach
void init() {
experienceService = new ExperienceService(
experienceRepository,
userRepository,
new FakeAuthorizationUtil(userId));
}
이렇게 환경을 설정해 줌으로써 가볍게 소형테스트를 진행할 수 있었습니다.