본문 바로가기
Spring/03. Service

02. Service Test

by 989898 2025. 2. 16.

1. 서비스 코드 (MemberService)

1.1 주요 역할

  • 비즈니스 로직 처리:
    • 회원 등록, 수정, 조회 등 핵심 로직을 처리.
  • 외부 의존성 주입:
    • MemberRepository: 데이터베이스와 상호작용.
    • PasswordEncoder: 비밀번호 암호화.

1.2 회원 등록 메서드 (registerMember)

public MemberResponse registerMember(MemberRegisterRequest memberRegisterRequest) {
    // 이메일 중복 체크
    if (memberRepository.existsByMbEmail(memberRegisterRequest.getMbEmail())) {
        throw new ConflictException("이미 사용 중인 이메일입니다.");
    }
    // 휴대폰 번호 중복 체크
    if (memberRepository.existsByMbMobile(memberRegisterRequest.getMbMobile())) {
        throw new ConflictException("이미 사용 중인 모바일 번호입니다.");
    }

    // 비밀번호 암호화 및 회원 객체 생성
    Member member = new Member(
        memberRegisterRequest.getMbEmail(),
        memberRegisterRequest.getMbName(),
        passwordEncoder.encode(memberRegisterRequest.getPassword()),
        memberRegisterRequest.getMbMobile()
    );
    memberRepository.save(member);

    // DTO 반환
    return new MemberResponse(
        member.getMbNo(), 
        member.getMbEmail(), 
        member.getMbName(), 
        member.getMbMobile(), 
        member.getCreatedAt()
    );
}

2. 테스트 코드 작성

2.1 테스트 목적

  • 서비스 메서드가 예상대로 동작하는지 확인.
  • 외부 의존성(Mock 객체)을 통해 독립적으로 테스트 수행.

2.2 회원 등록 테스트

@Test
@DisplayName("회원등록")
void registerMember() {
    // 1. 테스트용 요청 객체 생성
    MemberRegisterRequest request = new MemberRegisterRequest(
        "marco@nhnacademy.com", "마르코", "a123456789!", "01012345678"
    );

    // 2. Mock 설정
    Mockito.when(memberRepository.existsByMbEmail(Mockito.anyString())).thenReturn(false);
    Mockito.when(memberRepository.existsByMbMobile(Mockito.anyString())).thenReturn(false);
    
    // save 호출 시 동작 정의 (mbNo를 1로 설정)
    Mockito.doAnswer(invocation -> {
        Member paramMember = invocation.getArgument(0);
        Field field = Member.class.getDeclaredField("mbNo");
        field.setAccessible(true);
        field.set(paramMember, 1L);
        return null;
    }).when(memberRepository).save(Mockito.any(Member.class));

    // 3. 서비스 호출
    MemberResponse response = memberService.registerMember(request);

    // 4. 결과 검증
    Assertions.assertAll(
        () -> Assertions.assertEquals(1L, response.getMbNo()),
        () -> Assertions.assertEquals("marco@nhnacademy.com", response.getMbEmail()),
        () -> Assertions.assertEquals("마르코", response.getMbName()),
        () -> Assertions.assertEquals("01012345678", response.getMbMobile()),
        () -> Assertions.assertNotNull(response.getCreatedAt())
    );

    // 5. Mock 호출 검증
    Mockito.verify(memberRepository, Mockito.times(1)).existsByMbEmail(Mockito.anyString());
    Mockito.verify(memberRepository, Mockito.times(1)).existsByMbMobile(Mockito.anyString());
    Mockito.verify(memberRepository, Mockito.times(1)).save(Mockito.any(Member.class));
}

2.3 예외 처리 테스트

@Test
@DisplayName("회원등록 - 이메일중복")
void registerMember_exception_case1() {
    // 1. Mock 설정: 이메일 중복 체크에서 true 반환
    Mockito.when(memberRepository.existsByMbEmail(Mockito.anyString())).thenReturn(true);

    // 2. 서비스 호출 및 예외 검증
    Assertions.assertThrows(ConflictException.class, () -> {
        memberService.registerMember(new MemberRegisterRequest(
            "marco@nhnacademy.com", "마르코", "a123456789!", "01012345678"
        ));
    });

    // 3. Mock 호출 검증: save는 호출되지 않아야 함
    Mockito.verify(memberRepository, Mockito.times(1)).existsByMbEmail(Mockito.anyString());
    Mockito.verify(memberRepository, Mockito.never()).save(Mockito.any(Member.class));
}

3. 테스트 코드와 서비스 코드의 연결

3.1 Mocking

  • 외부 의존성을 제거하고 가짜(Mock) 객체를 사용하여 테스트.
  • 주요 메서드:
    • Mockito.when(): 특정 메서드 호출 시 반환값 설정.
    • Mockito.doAnswer(): 특정 메서드 호출 시 동작 정의.
    • Mockito.verify(): 특정 메서드 호출 여부 및 횟수 검증.

3.2 Assertions

  • 결과 값이 예상 값과 일치하는지 확인.
  • 주요 메서드:
    • Assertions.assertEquals(expected, actual): 값 비교.
    • Assertions.assertThrows(exceptionClass, executable): 예외 발생 여부 확인.
    • Assertions.assertAll(): 여러 검증을 그룹화하여 실행.

3.3 서비스 메서드 호출

  • 실제 비즈니스 로직을 실행하여 결과를 검증.
MemberResponse response = memberService.registerMember(request);
 

4. 주요 포인트 요약

항목 설명
Mock 설정 Mockito.when()으로 외부 의존성(Mock 객체)의 동작을 정의합니다.
서비스 호출 memberService.registerMember()와 같은 실제 로직을 실행합니다.
결과 검증 Assertions를 사용해 반환된 결과가 예상과 일치하는지 확인합니다.
예외 처리 테스트 Assertions.assertThrows()로 예외 발생 여부를 확인합니다.
Mock 검증 Mockito.verify()로 Mock 객체의 특정 메서드가 적절히 호출되었는지 확인합니다.
 

5. 간단한 흐름 요약

  1. 회원 등록 성공 테스트:
    • Mock 설정 → 서비스 호출 → 결과 검증 → Mock 호출 검증.
  2. 회원 등록 실패 테스트 (예외 처리):
    • Mock 설정 (중복 조건) → 서비스 호출 → 예외 발생 확인 → Mock 호출 검증.

6. 결론

  • 이 테스트 코드는 서비스 계층의 비즈니스 로직을 철저히 검증하며, 외부 의존성을 제거하기 위해 Mock 객체를 활용합니다.
  • 이를 통해 코드의 안정성과 유지보수성을 높이고, 다양한 예외 상황에 대한 방어적 코드를 보장할 수 있습니다.

'Spring > 03. Service' 카테고리의 다른 글

01. Service 개발  (0) 2025.02.14