이전에 Repository 기능을 테스트 할 때는 package와 class를 직접 만들었다.
테스트 클래스를 자동으로 생성하는 법을 알아보자.
테스트 해보고싶은 class에서 ctrl + shift + T 를 누른 후 Create new Test를 클릭해주면 아래와 같은 창이 뜬다. 테스트 하고싶은 Member를 선택 후 생성해주면 test 폴더 내에 package와 class가 생성되는걸 확인할 수 있다.

MemberService 를 테스트하기 위해 객체를 생성해주자.
class MemberServiceTest {
MemberService memberService;
}
given, when, then 패턴은 test 코드 작성시 자주 사용되는 패턴이다.
given(어떤 상황이 주어져서)
테스트를 준비하는 과정이다.
테스트에서 사용되는 변수, 입력 값들을 정의하거나, 객체를 정의하는 부분이다.
when(실행했을 때)
실제로 테스트를 실행하는 과정이다.
then(결과)
실행 결과를 검증하는 과정이다.
주로 assertThat 구문을 사용하여 예상 값(expected)과 실제 값(actual)을 비교한다.
테스트 코드가 길어질 시 한 눈에 알아보기 쉽게 주석처리를 통해 코드를 작성하는 것이 좋은 방법이다.
@Test
void 회원가입() {
//given 뭔가 주어져서
Member member = new Member(); // 멤버 객체 생성
member.setName("spring"); // 멤버에 spring
//when 실행했을때
Long saveId = memberService.join(member); // 회원가입시 아이디가 나오도록 해놓았음
//then 결과
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName()); // 멤버의 이름이 findMember의 이름과 같은지 확인
}
위와 같은 코드로 테스트는 통과하지만 중복 회원 가입을 테스트 해야한다.
새로운 test 메서드를 작성하여 이름이 같은 두 회원을 회원가입 해보도록 하자.
첫번째로, try-cathc 문을 이용하여 작성해보았다.
@Test
public void 중복_회원_예외(){
// given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
// when
memberService.join(member1);
//then
try{
memberService.join(member2);
fail();
}catch (IllegalStateException e){
// assertThat을 통해 두 객체가 같은지 확인한다.
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
두 번째로, assertThrow를 사용해보자. assertThrow 란 jUnit에 있는 메서드로
assertThrows(Class expectedType, Executable executable)의 기본 형태를 가지고 있다. executable block이나 lambda식을 실행했을 때, expectedType의 예외가 발생했는지 확인한다.
예외가 발생했다면, 예외 객체를 반환하는데, 반환된 객체를 이용하여 여러가지 확인을 할 수 있다.
() -> memberService.join(member2) 라는 람다를 실행했을 때, IllegalStateException 이 발생한다면 통과할것이다. 그리고 IllegalStateException 변수 e에 저장한다.
assertThat을 통해 e에 저장된 메세지가 "이미 존재하는 회원입니다."와 일치하는지 확인한다.
@Test
public void 중복_회원_예외(){
// given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
// when
memberService.join(member1);
//then
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
테스트를 실행해보면 정상 작동하는것을 알 수 있다.
repository 테스트 때와 마찬가지로 각 메서드가 끝났을 시 데이터를 clear 해줘야 한다.
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memoryMemberRepository;
@AfterEach
public void afterEach(){
memberRepository.clearStore();
}
...
}
테스트 코드를 다 작성하니 문제점이 생겼다.
MemberService에서 new를 통해 MemoryMemberRepository를 새로 생성하고 있기 때문에, 여기서 생성한 repository 객체가 같은 객체가 아닐수도 있어진다.
우리는 MemoryMemberRepository에 static으로 선언하였기 때문에 문제가 되지 않지만, static이 아니라면 저장소가 달라지게 된다.
같은 인스턴스를 사용하도록 바꿔보자.
우선 MemberService 클래스에 new 를 통해 MemberRepository를 생성했던 코드 부분을 지워준다. 그리고 memberRepository를 매개변수로 받는 생성자를 만들어 준다. 이렇게 하면 외부에서 객체를 받아서 넣어주는 방식으로 바꾸었다.
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
MemberServiceTest 클래스에서 MemberService 객체를 생성할 때, memoryRepository를 매개변수로 전달해야한다. 그리고 테스트는 독립적으로 실행되어야 하기에 @BeforeEach를 통해 memberRepository와 memberService 객체를 생성해준다.
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach(){
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
...
}
이러한 방법을 DI 즉, Dependence Injection 이라고 한다.