[Spring] 스프링 입문 웹 개발 기초(13)
회원 서비스 테스트
테스트 코드 생성
만들었다면 이제 테스트 코드를 적어볼텐데 굳이 영어권 사람들과 프로젝트를 하는 것이 아니라면 테스트 코드는 빌드에 들어가지 않으므로 함수 이름을 한글로 적어줘도 된다.
join = 회원가입 이런 형식으로 바꿔줘도 된다는 뜻이다.
코드 형식
이제 회원가입을 만들어볼 것인데, 형식을 맞춰서 하는 것이 좋다.
given, when, then = “무엇인가 주어졌는데, 이걸 실행했을 때, 결과가 이게 나와야 해” 하는 형식이다.
하지만 그냥 절차대로 코딩한다고 이해하는게 넓은 범주로 보면 편할 것 같다. 저 의미에 국한되지 않고 이해하자.
회원가입 만들기
이제 회원가입을 만들어보자.
- 주어진 것 : 멤버를 새로 만들고 이름을 저장한다.
- 검증하고 싶은 것 : memberService로 member를 저장하고 리턴값으로 나오는 Id를 saveId에 저장한다.
- 검증 방법 : saveId로 memberService에 저장한 Name과 member의 Name이 같은지 검증한다.
MemberService memberService = new MemberService();
@Test
void 회원가입() {
//given
Member member = new Member();
member.setName("hello");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
중복회원예외 테스트 (Try-catch)
하지만 이렇게 테스트 코드를 짠다면 너무 평범한 테스트 코드라고 할 수 있다.
테스트 코드를 사용할 때는 예외로 발생하는 부분을 테스트하는 것이 가장 중요하다.
그래서 중복회원예외의 경우를 테스트 해봐야한다.
try-catch문을 이용하여 중복회원을 테스트한다.
MemberService에서 join 메서드에 들어가있는 중복 회원 검증 기능은 validateDuplicateMember인데 IllegalStateException을 이미 존재하는 회원입니다.로 던지기에 catch에도 IllegalStateException으로 잡아준다.
@Test
public void 중복회원예외(){
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//when
memberService.join(member1);
try{
memberService.join(member2);
fail();
}catch (IllegalStateException e){
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
//then
}
중복회원테스트 (assertThrows)
하지만 try-catch문이 애매할 경우 assertThrows를 사용한다.
아까 사용했던 try-catch문은 잠시 주석처리해두고 짜보자.
assertThrows(IllegalStateException.class, () -> memberService.join(member2));
코드를 설명하자면 오른쪽에 있는 람다식 로직을 태우면 왼쪽에 IllegalStateException예외가 터져야한다는 뜻이다.
나는 강의에서와 다르게 assertThrows가 바로 인식이 안되었는데, 그 이유는 import static org.junit.jupiter.api.Assertions.*assertThrows*;
를 import 해줘야했다.
만약 나처럼 되지 않는 사람들은 앞에 Assertions.assertThrows로 만들고 alt + enter를 이용하여 편하게 import를 해주는 것이 좋을 것이다.
그럼 이렇게 생각할 수 있다.
이전 try - catch문을 사용할 경우에는 문자열을 비교할 수 있었는데, assertThrows를 사용하면 불가능한가?
가능하다. Ctrl + Alt + V 를 눌러주면 자동으로 변수를 만들어준다는 꿀팁을 다시 사용하면 IllegalStateException으로 선언될 것이다.
그것을 비교해주면 된다.
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
이제 이전에 했던 테스트코드와 마찬가지로 clear를 해줘야한다.
MemoryMemberRepository memberRepository= new MemoryMemberRepository();
@AfterEach
public void afterEach(){
memberRepository.clearStore();
}
이제는 회원가입에 있던 setName의 hello를 spring으로 바꾸더라도 db가 모두 날라갔기에 전체 수행에 문제가 없다. 한번 spring으로 바꾸고 전체 코드를 실행하자.
Dependency Injection
여기서 근데 굳이 memberRepository 를 새로 만들어서 사용해야하냐는 의문점이 생긴다.
MemberService로 이동하자.
먼저 MemberService에 new 하고 있는 MemberRepository를 고쳐줘야한다.
그리고 나서 Alt + Insert를 통해 생성자를 만들어준다.
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
이렇게 바뀌면 MemberService에서 직접 new 하지 않고 외부에서 memberRepository를 넣어주는 형식이 된다.
이런것을 우리는 의존성 주입(Dependency Injection)이라고 한다.
이제 다시 MemberServiceTest로 이동하자.
MemberService와 MemoryMemberRepository의 new를 제거해준다.
다음 @BeforeEach를 이용해 시작하기 전마다 실행하는 코드를 짜준다.
그 안에서 memberRepository의 new를 하고, memberService 안에 new 했던 memberRepository를 넣어준다.
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach(){
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
최종코드
MemberServiceTest.java
import mess.hellospring.domain.Member;
import mess.hellospring.domain.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach
public void beforeEach(){
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach(){
memberRepository.clearStore();
}
@Test
void 회원가입() {
//given
Member member = new Member();
member.setName("spring");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
public void 중복회원예외(){
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//when
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
/*try{
memberService.join(member2);
fail();
}catch (IllegalStateException e){
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}*/
//then
}
@Test
void 회원찾기() {
}
@Test
void findOne() {
}
}
MemberService.java
import mess.hellospring.domain.Member;
import mess.hellospring.repository.MemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/**
* 회원 가입
*/
public Long join(Member member){
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
Leave a comment