티스토리 뷰

본 포스팅은 인프런 김영한 강사님의 스프링 부트 입문 강의 섹션 3 <회원 관리 예제 - 백엔드 개발>을 수강하고 배운 점을 정리했습니다.

 

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., 스프링 학습 첫 길잡이! 개발 공부의 길을 잃지 않도록 도와드립니다. 📣 확인해주세

www.inflearn.com

 

[1] 회원 가입 메서드에서 '중복 회원 검색' 코드의 발전 과정

hello\hellospring\service\MemberService에서 회원 가입 메서드인 'join'의 코드 발전 과정을 살펴보자

우선 join의 초기 모습은 아래와 같다. 

1. join 코드 발전 첫 단계

첫 번째 줄에 Optional에 주목하자.

개발을 할 때 가장 많이 발생하는 예외 중 하나가 바로 NPE(NullPointerException)이다.

NPE를 피하기 위해 과거에는 일일이 'If(null)...'이런 식으로 null 여부를 검사하는 코드를 추가했는데, 이는 프로그램 크기가 클수록 번거롭고 복잡하다. 이때 Java8에서 등장한 Optional <T>는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와주고 유용한 메서드도 제공한다.

여기서 우리가 사용한 메서드는 'ifPresent'로 Optional 객체가 Null값이 아닌 존재할 경우라면 조건문을 실행하는 메서드이다. 

 

2. join 코드 발전 두 번째

첫 번째에서 Optional이 보기 안 좋다면 선언부를 지우고 바로 사용해도 무방하다. 첫 번째 코드에서 선언부 오른편에 위치했던 memberRepository.findByName(member.getName())에 바로 ifPresent를 붙여서 코드를 이었다.

 

3. join 발전 마지막 단계

두 번째 코드의 단점은 이어붙인 코드가 너무 길어서 눈에 명확하게 안 들어오는 점이다. 이를 해결하기 위해 findByName을 따로 메서드로 만들었다. validateDuplicateMember 메서드를 사용해서 코드를 간결하게 만들었다. 

 

 

 

[2] Test 코드 자동 생성 하기

개발한 기능을 실행해서 테스트할 때 자바의 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해서 해당 기능을 사용한다. 하지만 이러한 방법은 준비부터 실행하는데 까지 오래 걸리고, 반복 실행이 어려우며, 여러 테스트를 한 번에 실행하기 어렵다는 단점이 있다. 자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다. 

 

 

1) (Window 기준) 테스트하고 싶은 클래스 이름에 마우스를 대면 형광등 모양이 나온다. 형광등을 누르면 여러 옵션들이 나오는데 이 중에서 'Create Test'를 선택한다. 

 

 

2) Create Test 창에서는 우리가 테스트할 클래스인 'MemberService'의 멤버 메서드들 중 어떤 것의 테스트 코드를 생성하고 싶은지 선택할 수 있다. 모두 선택한 뒤, OK를 누른다. 

 

 

3) 그러면 이렇게 Service 패키지 안에 MemberServiceTest라는 자바 클래스가 자동 생성된다. 

 

 

4) Test를 위한 메서드는 아래와 같이 구성하면 된다. 

차례대로 given, when, then 세 단계로 구성하는 것이 이상적인 테스트 코드이다. 첫 번째 'given'은 '주어진 조건'으로 기반이 되는 데이터를 제시한다. 다음 'when' 부분은 '테스트가 실행되는 상황'을 작성한다. 즉, 검증하고자 하는 상황을 입력한다. 마지막으로 'then'에서는 테스트가 성공한다면 도출될 '올바른 결과'를 제시한다. 

 

예시 코드는 아래와 같다.

@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());
 }

+ 테스트 코드는 내부적으로 실행하는 것이므로 명확성을 위해 위와 같이 서드명을 한국어로 입력해도 무방하다고 한다. 

 

 

[3] Test 코드 순차 시행 미 보장으로 인한 오류와 해결 방법: @AfterEach

한 번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남을 수 있다. 이렇게 되면 이전 테스트 때문에 다음 테스트가 실패할 가능성이 있다. 아래의 예시와 함께 이해해 보자.

 

첫 번째 코드는 'MemoryMemberRepositoryTest' 클래스에 있는 'findAll()' 테스트 메서드이다. 

@Test
 public void findAll() {
 	//given
	 Member member1 = new Member();
 	member1.setName("spring1");
 	repository.save(member1);
 	Member member2 = new Member();
 	member2.setName("spring2");
 	repository.save(member2);
    
 	//when
 	List<Member> result = repository.findAll();
    
 	//then
 	assertThat(result.size()).isEqualTo(2);
 }

 

아래에 두 번째 코드는 'MemoryMemberRepositoryTest' 클래스에 있는 'findByName()' 테스트 메서드이다. 

 @Test
 public void findByName() {
 	//given
 	Member member1 = new Member();
 	member1.setName("spring1");
 	repository.save(member1);
 	Member member2 = new Member();
 	member2.setName("spring2");
 	repository.save(member2);
    
 	//when
 	Member result = repository.findByName("spring1").get();
    
 	//then
	assertThat(result).isEqualTo(member1);
 }

두 메서드는 모두 'MemoryMemberRepositoryTest'에 있고, 'given' 조건부에서 테스트를 위해 멤버 객체를 생성해 레파지토리에 세이브하는 코드를 구성하는 공통점이 있다. 이때, 각 매서드에서는 멤버 이름을 "spring1"과 "spring2"로 똑같이 생성했다. 

이 테스트를 돌린다면, 각 테스트 메서드들의 실행 순서를 보장하지 않으므로 메모리 DB에 직전 테스트의 결과가 남을 수 있다. 그럼 뜻하지 않게 이전 테스트로 인해 다음 테스트가 실패한다. (같은 이름의 멤버가 이미 DB에 존재하므로)

 

이를 해결하기 위해 테스트 하나가 끝나면 데이터 레파지토리를 지워주는 코드를 넣어야 한다. 이것이 바로 @AfterEach이다.  @AfterEach를 사용하면 각 테스트가 종료될 때마다 이 기능을 실행한다. 여기서는 메모리 DB에 저장된 데이터를 삭제한다.

 

+ 테스트는 각각 독립적으로 실행되어야 한다. 테스트 순서에 의존관계가 있는 것은 좋은 테스트가 아니다.  

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
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
글 보관함