[Spring Boot] 자바의 DI를 사용한 회원 서비스와 테스트

DU·2024년 7월 17일

Spring Boot

목록 보기
4/4
post-thumbnail

자바의 DI를 사용한 회원 서비스와 테스트

이번 예제에서는 자바의 DI(Dependency Injection, 의존성 주입)를 사용하여 회원 서비스의 코드를 리팩토링하고, 이를 테스트하는 방법을 살펴보겠습니다.

기존 코드

먼저 기존의 회원 서비스 코드를 살펴보겠습니다. 이 코드는 회원 서비스가 MemoryMemberRepository를 직접 생성합니다.

public class MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
}

이 방식은 MemberService가 특정 구현체(MemoryMemberRepository)에 강하게 결합되어 있어서 유연성이 떨어집니다. 이를 DI를 사용하여 개선해보겠습니다.

DI를 사용한 코드

DI를 사용하여 MemberService 클래스의 의존성을 주입받도록 변경합니다. 이렇게 하면 테스트나 실제 애플리케이션에서 쉽게 MemberRepository의 다른 구현체를 주입할 수 있습니다.

public class MemberService {
    private final MemberRepository memberRepository;

    // 생성자를 통해 MemberRepository 의존성을 주입받는다
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    // ... 나머지 코드
}

물론입니다. 주어진 MemberServiceTest 코드의 각 부분을 더 상세히 분석하여 설명하겠습니다.

전체 구조

이 테스트 클래스는 MemberServiceMemoryMemberRepository의 동작을 검증합니다. DI(Dependency Injection)를 통해 MemberServiceMemoryMemberRepository를 주입받도록 하고, 각 테스트는 독립적으로 실행되도록 설정합니다.

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterEach;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach() {
        // 각 테스트 전에 새로운 MemoryMemberRepository를 생성하고
        memberRepository = new MemoryMemberRepository();
        // 이를 MemberService에 주입한다.
        memberService = new MemberService(memberRepository);
    }

    @AfterEach
    public void afterEach() {
        // 각 테스트 후에 저장소를 초기화하여 테스트 간의 상호 영향을 방지한다.
        memberRepository.clearStore();
    }

    @Test
    public void 회원가입() throws Exception {
        // Given: 테스트를 위한 데이터를 준비한다.
        Member member = new Member();
        member.setName("hello");

        // When: 테스트하려는 동작을 실행한다.
        Long saveId = memberService.join(member);

        // Then: 결과를 검증한다.
        Member findMember = memberRepository.findById(saveId).get();
        assertEquals(member.getName(), findMember.getName());
    }

    @Test
    public void 중복_회원_예외() throws Exception {
        // Given: 테스트를 위한 데이터를 준비한다.
        Member member1 = new Member();
        member1.setName("spring");

        Member member2 = new Member();
        member2.setName("spring");

        // When: 첫 번째 회원을 가입시킨다.
        memberService.join(member1);

        // When: 두 번째 회원을 가입 시도할 때 예외가 발생하는지 확인한다.
        IllegalStateException e = assertThrows(IllegalStateException.class, 
            () -> memberService.join(member2)); // 예외가 발생해야 한다.

        // Then: 예외 메시지를 검증한다.
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
    }
}

코드 분석

클래스 멤버 변수

MemberService memberService;
MemoryMemberRepository memberRepository;

MemberServiceMemoryMemberRepository 객체를 테스트 클래스의 멤버 변수로 선언합니다. 이 변수들은 각각 테스트 메서드에서 사용할 인스턴스들을 저장합니다.

@BeforeEach 메서드

@BeforeEach
public void beforeEach() {
    // 각 테스트 전에 새로운 MemoryMemberRepository를 생성하고
    memberRepository = new MemoryMemberRepository();
    // 이를 MemberService에 주입한다.
    memberService = new MemberService(memberRepository);
}
  • @BeforeEach: 이 어노테이션이 붙은 메서드는 각 테스트 메서드 실행 전에 호출됩니다.
  • memberRepository = new MemoryMemberRepository();: 각 테스트 전에 새로운 MemoryMemberRepository 인스턴스를 생성합니다.
  • memberService = new MemberService(memberRepository);: 생성된 MemoryMemberRepository 인스턴스를 MemberService 생성자에 주입합니다.

이 과정은 각 테스트가 독립적으로 실행되도록 보장합니다.

@AfterEach 메서드

@AfterEach
public void afterEach() {
    // 각 테스트 후에 저장소를 초기화하여 테스트 간의 상호 영향을 방지한다.
    memberRepository.clearStore();
}
  • @AfterEach: 이 어노테이션이 붙은 메서드는 각 테스트 메서드 실행 후에 호출됩니다.
  • memberRepository.clearStore();: 저장소를 초기화하여 테스트 간 데이터가 서로 영향을 미치지 않도록 합니다.

회원가입 테스트

@Test
public void 회원가입() throws Exception {
    // Given: 테스트를 위한 데이터를 준비한다.
    Member member = new Member();
    member.setName("hello");

    // When: 테스트하려는 동작을 실행한다.
    Long saveId = memberService.join(member);

    // Then: 결과를 검증한다.
    Member findMember = memberRepository.findById(saveId).get();
    assertEquals(member.getName(), findMember.getName());
}
  • @Test: 이 어노테이션이 붙은 메서드는 JUnit에 의해 테스트 메서드로 인식됩니다.
  • Given: 테스트를 위한 초기 데이터를 설정합니다.
    • Member member = new Member();: 새로운 Member 객체를 생성합니다.
    • member.setName("hello");: member 객체의 이름을 "hello"로 설정합니다.
  • When: 테스트하려는 동작을 실행합니다.
    • Long saveId = memberService.join(member);: memberService를 통해 member 객체를 가입시킵니다.
  • Then: 결과를 검증합니다.
    • Member findMember = memberRepository.findById(saveId).get();: 저장소에서 가입된 회원을 찾아옵니다.
    • assertEquals(member.getName(), findMember.getName());: 가입된 회원의 이름이 원래의 member 객체의 이름과 같은지 확인합니다.

중복 회원 예외 테스트

@Test
public void 중복_회원_예외() throws Exception {
    // Given: 테스트를 위한 데이터를 준비한다.
    Member member1 = new Member();
    member1.setName("spring");

    Member member2 = new Member();
    member2.setName("spring");

    // When: 첫 번째 회원을 가입시킨다.
    memberService.join(member1);

    // When: 두 번째 회원을 가입 시도할 때 예외가 발생하는지 확인한다.
    IllegalStateException e = assertThrows(IllegalStateException.class, 
        () -> memberService.join(member2)); // 예외가 발생해야 한다.

    // Then: 예외 메시지를 검증한다.
    assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
  • Given: 테스트를 위한 초기 데이터를 설정합니다.
    • Member member1 = new Member();: 첫 번째 Member 객체를 생성합니다.
    • member1.setName("spring");: member1 객체의 이름을 "spring"으로 설정합니다.
    • Member member2 = new Member();: 두 번째 Member 객체를 생성합니다.
    • member2.setName("spring");: member2 객체의 이름을 "spring"으로 설정합니다.
  • When: 테스트하려는 동작을 실행합니다.
    • memberService.join(member1);: 첫 번째 회원을 가입시킵니다.
    • IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));: 두 번째 회원을 가입 시도할 때 IllegalStateException 예외가 발생하는지 확인합니다.
  • Then: 결과를 검증합니다.
    • assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");: 예외 메시지가 예상된 메시지("이미 존재하는 회원입니다.")와 같은지 확인합니다.

결론

이 테스트 클래스는 MemberServiceMemoryMemberRepository의 기능을 검증합니다. DI를 통해 유연하게 의존성을 주입받아 테스트할 수 있도록 설계되어 있으며, 각 테스트는 독립적으로 실행되어 다른 테스트와의 상호작용을 방지합니다. @BeforeEach@AfterEach를 사용하여 테스트 전후의 설정과 정리를 수행하여 테스트의 신뢰성을 높이고 있습니다.

0개의 댓글