일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- wrapper
- Short
- 컬렉션
- 무한
- Java
- 박싱
- Jenkins
- Scanner
- bootstrap
- 메소드
- 싱글톤
- 클래스
- unboxing
- 스프링
- 내장객체
- 싱글턴
- 루프
- start.spring.io
- https://start.spring.io
- dependency
- suvlet
- 인텔리제이
- boxing
- 언박싱
- 콜렉션
- 자동형변환
- 제네릭
- maven
- 빌드
- 제너릭
Archives
- Today
- Total
Developer Gonie
[스프링 부트] 13. 회원 서비스 테스트 본문
이번 게시물 흐름 요약
service 패키지 위치에서 생성한 MemberService 클래스 내의 메서드 들을 테스트 한다.
특히 join (회원가입) 메소드에서 중복 이름을 가진 회원을 등록하려는 경우 예외가 발생 하는지,
그리고 그 예외에서 발생시킨 에러 메세지가 예측한 것과 같은지를 체크함
테스트 코드 관련해서 추가적으로 알려주신 부분
* 테스트 코드 기본틀을 자동으로 생성하는 방법
클래스 내부 - 오른쪽 마우스 - Generate... - Test..., Testing library는 JUnit5 선택, 테스트 코드 작성할 메소드 선택 후 OK
이러면 내가 지정한 클래스의 폴더구조까지 같게 테스트 클래스가 생성된다.
* 테스트 메소드의 이름은 과감하게 한글로 바꿔도 된다.
실제 프로덕션 코드에 대해서는 메서드 혹은 변수에 한글로 이름을 적기가 애매한데
테스트 코드에서는 영어권 사람하고 일하는게 아니면 직관적으로 쉽게 알 수 있기 위해 한글로도 많이 적는다고 한다.
또한, 테스트 코드들은 프로젝트를 빌드시켰을 때 여기에 포함되지 않는다고 한다.
* 테스트 메소드의 작성시 유용한 given, when, then 주석틀
테스트 코드 작성시 아래와 같이 주석을 적어두고 시작하는 방법을 추천하셨다.
코드 작성시에 뭘 해야하는 지 알기 쉽고, 코드가 길때 이해하기도 쉽다.
물론 이 패턴에 맞지 않는 경우도 있는데 시작을 이렇게 해보라는 것이지 나중엔 실력에 맞게 변형해 작성할 수 있게 될 것이다.
// given : 뭔가가 주어졌는데
// when : 이걸 실행했을 때
// then : 이런 결과가 나와야 해
1. service 패키지에 있는 MemberService 클래스 메소드들 테스트 코드 작성
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
@BeforeEach // 같은 memberRepository 객체를 사용하도록 수정된 코드
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
// MemberService 클래스 입장에서 외부에서 new 하여 생성된 객체를 내부의 멤버변수에 넣어주고 있는데
// 이런것을 dependency injection(DI) 이라고 함.
}
@AfterEach
public void afterEach(){
memberRepository.clearStore();
}
// 아래와 같이 해줬던 이유는 공유변수 초기화를 위해 clearStore() 메소드를 사용해야 하는데
// MemberService의 멤버변수인 memberRepository가 private 인데 이를 접근할 수 있는 멤버함수가 없어서
// MemoryMemberRepository의 내부에 선언된 HashMap은 static이라서 모든 객체가 공유하기에 아래와 같이 해줄 수 있었음.
// 그런데 이 과정에서 엄연히 서로 다른 객체를 사용하고 있다는 것은 바람직 하지 않아 MemberService의 생성자를 수정하며 위와 같이 수정함.
// MemberService memberService = new MemberService();
// MemoryMemberRepository memberRepository = new MemoryMemberRepository();
@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
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 catch 문으로 아래와 같이 해볼 수 있겠으나 더욱 편리한 위의 문법이 제공됨
// try{
// memberService.join(member2); // 예외가 발생해야 하는 곳
// fail(); // 예외가 발생하지 않고 여기까지 왔다면 문제가 있는것으로 처리.
// } catch(IllegalStateException e){
// assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
// }
//then
}
@Test
void findMembers() {
}
@Test
void findOne() {
}
}
2. 테스트 말고 일반 service 패키지에 있는 MemberService 클래스 코드 일부 수정
// 기존코드
private final MemberRepository memberRepository = new MemoryMemberRepository();
// 외부에서 MemoryMemberRepository 객체를 받도록 수정된 코드
private final MemberRepository memberRepository;
MemberService(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
// 기존코드
//private final MemberRepository memberRepository = new MemoryMemberRepository();
// 외부에서 MemoryMemberRepository 객체를 받도록 수정된 코드
private final MemberRepository memberRepository;
MemberService(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
/*
* 회원가입
* */
public Long join(Member member){
//같은 이름이 있는 중복 회원이 있다면 예외를 발생시킴
// 직관적인 코드
// Optional<Member> result = memberRepository.findByName(member.getName());
// result.ifPresent(m -> { // ifPresent : 만약 값이 있다면 즉, null이 아니라면 실행부를 실행하라.
// throw new IllegalStateException("이미 존재하는 회원입니다.");
// });
//위 코드를 간소화 시킨것
// memberRepository.findByName(member.getName())
// .ifPresent(m -> {
// throw new IllegalStateException("이미 존재하는 회원입니다.");
// });
//바로 위 코드를 드래그 해서 Alt + Enter를 눌러 extract method 하여 생성한 메소드
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();
}
/*
* 1개 회원 조회
* */
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
'인프런 김영한님 강의 > 1 . 스프링 부트 입문' 카테고리의 다른 글
[스프링 부트] 14-2. 스프링빈을 등록하는 방법2 (0) | 2022.06.27 |
---|---|
[스프링 부트] 14. 스프링빈을 등록하는 방법1 (0) | 2022.06.27 |
[스프링 부트] 12. 회원 서비스 개발 (0) | 2022.06.26 |
[스프링 부트] 11. 회원 리포지토리 테스트 케이스 작성 (0) | 2022.06.26 |
[스프링 부트] 10. 회원 도메인과 리포지토리 만들기 (0) | 2022.06.23 |
Comments