Develop

[Spring Boot] 게시판 - 06. 단위 테스트 작성하기 (Mockito & AssertJ) 본문

백엔드/게시판 만들기

[Spring Boot] 게시판 - 06. 단위 테스트 작성하기 (Mockito & AssertJ)

230801 2025. 11. 24. 04:04

안녕하세요  .ᐟ 

 

오늘은 Spring Boot 프로젝트에서 단위 테스트를 작성하는 방법을 정리합니다.

 

JUnit 5Mockito를 활용해 Service 레이어의 비즈니스 로직을 테스트했고,  given-when-then 패턴을 적용해 가독성 높은 테스트 코드를 작성해봤습니다~

 

 


 

단위 테스트란?

  • 단위 테스트는 애플리케이션의 가장 작은 단위를 독립적으로 검증하는 테스트입니다.
  • Spring Boot에서는 주로 Service 레이어의 메서드를 테스트합니다.

 

테스트 종류

구분 범위 특징
단위 테스트 개별 메서드 Mock 사용, 빠른 실행
통합 테스트 여러 컴포넌트 실제 DB 연동
E2E 테스트 전체 시스템 실제 사용자 시나리오

 

 


 

테스트 환경 구성

build.gradle
dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

포함된 라이브러리:

  • JUnit 5: 테스트 프레임워크
  • Mockito: Mock 객체 생성
  • AssertJ: 검증 라이브러리

 

사용되는 어노테이션들

@ExtendWith(MockitoExtension.class) // JUnit5 에서 Mockito를 사용할 수 있게 활성화함
class UserServiceTest {
    
    @Mock // 가짜 객체를 생성함
    private UserRepository userRepository;
    
    @InjectMocks // 테스트 대상 객체를 생성하고, @Mock 객체들을 자동으로 주입함 (실제 DB 연결 안해도됨)
    private UserService userService;
    
    @BeforeEach // 각 테스트 실행전에 매번 실행됨 (코드 중복 제거)
    void setUp() { }
    
    @Test
    @DisplayName("회원가입 성공") // 테스트 이름을 사람이 읽기 쉽게 표시함
    void createUser_Success() { }
}

 

 


 

given-when-then 패턴

테스트 코드를 3단계로 구조화하는 BDD (Behavior-Driven Development) 스타일 패턴입니다.

코드의 "행동"을 중심으로 테스트를 작성하는 방법론 으로, "~할때 ~ 하면 ~ 이어야 한다" 흐름으로 작성합니다.

 

 

TDD vs BDD

구분 TDD BDD
초점 코드가 제대로 작동하는가 행동이 올바른가
언어 기술적 (assertEquals) 자연어에 가까움 (should)

 

@Test
void createUser_Success() {
    // given: 테스트 준비
    given(userRepository.findByEmail(anyString())).willReturn(Optional.empty());
    
    // when: 테스트 실행
    User result = userService.create(request);
    
    // then: 결과 검증
    assertThat(result).isNotNull();
}

 

각 단계의 역할:

  • given: Mock 동작 정의, 데이터 준비
  • when: 테스트 대상 메서드 실행
  • then: 결과 검증

 


 

실전 테스트 작성

회원가입 테스트

@Test
@DisplayName("회원가입 성공")
void create_Success() {
    // given
    given(userRepository.findByName(anyString())).willReturn(Optional.empty());
    given(userRepository.findByEmail(anyString())).willReturn(Optional.empty());
    given(passwordEncoder.encode(anyString())).willReturn("encodedPassword");
    given(userRepository.save(any(User.class))).willReturn(user);

    // when
    User result = userService.create(createRequest);

    // then
    assertThat(result).isNotNull();
    assertThat(result.getName()).isEqualTo("홍길동");
    then(userRepository).should().save(any(User.class));
}

 

실패 케이스

@Test
@DisplayName("회원가입 실패 - 중복된 닉네임")
void create_Fail_DuplicateNickname() {
    // given
    given(userRepository.findByName(anyString())).willReturn(Optional.of(user));

    // when & then
    assertThatThrownBy(() -> userService.create(createRequest))
        .isInstanceOf(DuplicateResourceException.class)
        .hasFieldOrPropertyWithValue("errorCode", ErrorCode.NICKNAME_ALREADY_EXISTS);
}

 

 

기타 테스트 케이스

given-when-then 형식으로 UserService의 테스트 케이스를 작성했습니다.

  • 회원가입 성공/실패 (중복 체크)
  • 전체 사용자 조회
  • ID로 사용자 조회 성공/실패
  • 사용자 수정 성공/실패 (비밀번호 검증)
  • 사용자 삭제 성공/실패

 


 

느낀 점

  • 실패 케이스 검증이 중요하다.
  • 프로젝트 구조를 바꾸고나서는 엔티티 스캔범위나 설정 파일도 함께 확인해야 한다.
  • 최근에 팀프로젝트 머지할때 test 부분이 오류나서 주석처리한적이 있었는데, 오늘 배운 mockito 를 이용해서 리팩토링 해봐야겠다.