의존성 역전을 활용하여 테스트코드를 경량화하는 방법

양말·2024년 6월 27일
3

SpringTestCode

목록 보기
7/10

첫 스프링 프로젝트를 혼자서 맡아 진행하면서, 디프만 스터디에서 배운 테스트코드 작성 방법을 적용해 보았습니다.

문제점

체험(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 인터페이스 추가

원래는 빨간 박스 형태였는데, 이러한 의존성은 테스트코드를 무겁게 만드므로 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));
    }

이렇게 환경을 설정해 줌으로써 가볍게 소형테스트를 진행할 수 있었습니다.

참고 레포

🏋️‍♂️ 와글 서버

profile
코끼리

0개의 댓글