
이번 예제에서는 자바의 DI(Dependency Injection, 의존성 주입)를 사용하여 회원 서비스의 코드를 리팩토링하고, 이를 테스트하는 방법을 살펴보겠습니다.
먼저 기존의 회원 서비스 코드를 살펴보겠습니다. 이 코드는 회원 서비스가 MemoryMemberRepository를 직접 생성합니다.
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
}
이 방식은 MemberService가 특정 구현체(MemoryMemberRepository)에 강하게 결합되어 있어서 유연성이 떨어집니다. 이를 DI를 사용하여 개선해보겠습니다.
DI를 사용하여 MemberService 클래스의 의존성을 주입받도록 변경합니다. 이렇게 하면 테스트나 실제 애플리케이션에서 쉽게 MemberRepository의 다른 구현체를 주입할 수 있습니다.
public class MemberService {
private final MemberRepository memberRepository;
// 생성자를 통해 MemberRepository 의존성을 주입받는다
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// ... 나머지 코드
}
물론입니다. 주어진 MemberServiceTest 코드의 각 부분을 더 상세히 분석하여 설명하겠습니다.
이 테스트 클래스는 MemberService와 MemoryMemberRepository의 동작을 검증합니다. DI(Dependency Injection)를 통해 MemberService가 MemoryMemberRepository를 주입받도록 하고, 각 테스트는 독립적으로 실행되도록 설정합니다.
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;
MemberService와 MemoryMemberRepository 객체를 테스트 클래스의 멤버 변수로 선언합니다. 이 변수들은 각각 테스트 메서드에서 사용할 인스턴스들을 저장합니다.
@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("이미 존재하는 회원입니다.");: 예외 메시지가 예상된 메시지("이미 존재하는 회원입니다.")와 같은지 확인합니다.이 테스트 클래스는 MemberService와 MemoryMemberRepository의 기능을 검증합니다. DI를 통해 유연하게 의존성을 주입받아 테스트할 수 있도록 설계되어 있으며, 각 테스트는 독립적으로 실행되어 다른 테스트와의 상호작용을 방지합니다. @BeforeEach와 @AfterEach를 사용하여 테스트 전후의 설정과 정리를 수행하여 테스트의 신뢰성을 높이고 있습니다.