9. 회원 서비스 테스트

Bummy·2023년 4월 27일
0
post-thumbnail

먼저 원하는 Service 클래스의 테스트 클래스를 만들어준다.

전에는 테스트 클래스를 직접 작성했지만 인텔리제이에서는 단축키를 제공한다.

<cmd + shift + T> 를 누르면 테스트 클래스를 자동으로 작성해준다.


MemberServiceTest.java

package hello.hellospring.sevice;

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.BeforeAll;
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;
import static org.junit.jupiter.api.Assertions.fail;

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){
//            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
//        }
        //then
    }

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}

given, when, then 패턴

given-when-then 패턴은 test code 작성 시 자주 사용하는 패턴이다.

어떤 상황이 주어져서(given), 이것을 실행 했을 때(when), 어떤 결과가 나와야한다(then)의 구조를 가지고 있다.

Given

  • 테스트를 위해 준비하는 과정
  • 테스트에서 사용되는 변수, 입력 값들을 정의하거나, Mock 객체를 정의하는 부분

When

  • 실제로 테스트를 실행하는 과정
  • 하나의 메서드만 수행하는 것이 바람직하며 대체로 가장 중요하지만 가장 짧은 과정이다.

Then

  • When에서 실행한 결과를 검증하는 과정. 예상 값과 실제 값을 비교한다.
  • 주로 assertThat 구문을 활용하여 검증

회원 가입(join) 테스트 코드

테스트를 어떤 방식으로 처리할 것인가?

  1. Member 객체 member를 생성하여 이름을 spring으로 설정한다.

  2. 위에서 생성한 객체를 Join을 통해 회원 서비스에 회원 가입한다.

    join은 회원 가입 후 회원의 id를 반환하므로 반환된 회원 id를 long 변수 saveId에 저장해놓는다.

  3. findOne을 통해 saveId가 가지고 있는 회원을 찾고, findMember에 저장한다.

  4. findMember와 member가 동일한 회원인지 확인한다.

    만약 동일한 회원이라면 join 기능이 제대로 작동한다는 것을 확인할 수 있다.

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

Given

  • 회원 객체 member에 spring이라는 이름을 가진 회원이 있다는 것이 지금의 주어진 상황
  • 따라서 member 객체를 생성하고 .setName을 통해 이름을 hello로 설정한다

When

  • member 객체를 join
  • join 메소드를 이용하여 memeber 객체를 회원가입하고, 결과로 회원의 id를 반환받아 saveId에 저장한다.

then

  • 회원 가입하고 받은 id에 해당하는 객체를 찾고, 위에서 만든 member 객체가 서로 같은지 확인하면 된다.
  • MemberService에서 만들었던 메소드인 findOne을 통해 saveId에 해당하는 member 객체를 받고 assertThat을 이용해 두 객체가 같은지 확인한다.

중복회원예외 테스트 코드

join은 정상적으로 작동하는 것을 확인할 수 있었지만 중복 회원을 막는 기능이 정상적으로 작동되어야한다. 일부러 이름이 같은 두 회원을 회원 가입 시켜서 예외가 발생하도록 해본다.

  @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){
//            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
//        }
        //then
    }

Given

  • 두 개의 회원 객체가 있고, 두 회원 모두 이름이 spring이다.

When

  • 두 회원을 회원가입 시킨다.

Then

  • 만약 예외가 정상적으로 터졌다면 MemberService의 중복회원예외 메소드가 예외 메시지로 설정한 “이미 존재하는 회원입니다.”가 나와야한다. 이 메시지가 정상적으로 나왔는지 확인한다.

발생한 예외를 두 가지 방법으로 확인해 볼텐데 첫 번째는 try-catch 문을 이용한 방법이고, 두 번째는 assertThat문을 이용한 방식이다.

try-catch

        try {
           memberService.join(member2);
           fail();
        }catch (IllegalStateException e){
            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
      }

try에 예외를 발생시킬 수 있는 코드를 작성하고

catch exception이 발생했을 때 예외를 처리하기 위한 코드를 작성해준다.

assertThat

assertThat은 junit에 있는 메소드로써, assertThrows(Class expectedType, Executable executable)의 기본 형태를 가지고 있다.

executable block이나 lambda식을 실행했을 때, expectedType의 예외가 발생했는지 확인한다.

예외가 발생했다면, 예외 객체를 반환하는데 반환된 객체를 이용하여 여러가지 확인을 할 수 있다.

코드에서 중복을 처리하기 위한 예외가 발생한다면 “이미 존재하는 회원입니다” 메시지가 발생해야한다.

@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));
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}

lambda 식을 실행했을 때 IllegalStateException이 발생한다면

변수 e에 담아서 해당 메시지가 “이미 존재하는 회원입니다.”인지를 확인한다.


Repository 비우기

 @AfterEach
    public void afterEach(){
        memberRepository.clearStore();
    }

각 테스트가 순서에 의존하지 않게 하기 위해 테스트가 끝나면 repository를 비워준다.


MemoryMemberRepository 통합하기

테스트 코드를 작성하다 보니 테스트 클래스마다 new 를 통해 MemoryMemberReposirtory 를 계속 생성하고 있었다.

이와 같은 경우 각 테스트 코드가 같은 repository 를 사용하고 있다고 보장할 수 없다.

  1. 우선 MemberService 클래스에서 MemberRepository를 new를 통해 생성했던 코드를 제거한다.

  2. memberRepository를 클릭 ⌘ + N 을 통해서 Add Constructor Parameter를 선택해준다.

    생성자에서 memberRepository를 매개변수로 받아서 변수에 넣어준다.

    이렇게 하면 new를 통해 객체를 하나 생성하는 것이 아니라, 외부에서 객체를 받아서 넣어주는 방식으로 변경된다.

  3. MemberServiceTest 클래스에서 MemberService 객체를 생성할 때, memberRepository를 매개변수로 전달한다.

    그리고 테스트 함수는 독립적으로 실행되어야 하기에 @BeforeEach를 활용해서 memberRepository와 memberService 객체를 생성한다.

    @BeforeEach
        public void beforeEach(){
            memberRepository = new MemoryMemberRepository();
            memberService = new MemberService(memberRepository);
        }

    MemberService 입장에서는 new를 이요해 인스턴스를 생성하지 않고 매개변수를 통해 외부 memberRepository를 전달해준다.

    MemberService 입장에서는 이것을 DI(Dependence Injection)라고 한다.


    Reference

0개의 댓글