jhhan의 블로그

Spring - Test Code 작성(2) 본문

Spring

Spring - Test Code 작성(2)

jhhan000 2020. 9. 13. 18:36

저번에 이어서 썼던 Test Code에 대해서 한번 더 다뤄보는 시간을 갖겠습니다.

당연한 것이지만, 코드는 이전 글에서 썼던 코드를 이어서 쓸 것입니다.

 

이번에는 회원가입을 하는 로직을 만들어보겠습니다.

프로젝트 구조를 보겠습니다.

service라는 패키지를 만들었고, MemberService라는 클래스를 만들었습니다.

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    
    /**
     * 회원 가입
     */
    public Long join(Member member) {
        /* 중복 회원 안됨 */
        Optional<Member> result = memberRepository.findByName(member.getName());
        result.ifPresent(m -> {
            throw new IllegalStateException("이미 존재하는 회원입니다.");
        });
//        memberRepository.findByName(member.getName())
//                .ifPresent(m -> {
//                    throw new IllegalStateException("이미 존재하는 회원입니다.");
//                });
        memberRepository.save(member);
        return member.getId();
    }

    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers() {
        return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberID) {
        return memberRepository.findById(memberID);
    }
}

코드는 위와 같이 적습니다.(회원가입 뿐만 아니라 회원 조회도 만들어 보았습니다.)

  • Optional로 이미 가입된 이름이 있는지 없는지 확인을 합니다.
  • result가 null이 아니라면 Exception을 던져서 회원 가입이 안되게 합니다.
  • 이 과정을 굳이 Optional을 쓰지 않고도 진행할 수 있는 코드는 주석처리를 했습니다.

또한 이러한 로직을 따로 빼서 진행을 할 수도 있습니다.

    /**
     * 회원 가입
     */
    public Long join(Member member) {
        /* 중복 회원 안됨 */
//        Optional<Member> result = memberRepository.findByName(member.getName());
//        result.ifPresent(m -> {
//            throw new IllegalStateException("이미 존재하는 회원입니다.");
//        });
//        memberRepository.findByName(member.getName())
//                .ifPresent(m -> {
//                    throw new IllegalStateException("이미 존재하는 회원입니다.");
//                });
        validateDuplicateMember(member); // 중복 회원 검증
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
                .ifPresent(m -> {
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }

이렇게 새로운 메소드를 만들어서 해당 메소드만 불러서 로직을 진행할 수도 있습니다.

 

그럼 이제 Test 코드를 만들어서 진행해보겠습니다.

test 폴더 밑에 다음과 같이 패키지와 Test Class를 만듭니다.
(혹은 Ctrl+Shift+T를 하면 자동으로 테스트 코드를 만들어주는 단축키도 있습니다. 활용해보세요!!)

 

그리고 Test Code를 적어봅니다.

회원가입부터 해보겠습니다.

    @Test
    //void 회원가입() -> 이렇게 한글로 적어도 된다.
    void join() {
        //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);
//        try{
//            memberService.join(member2);
//            fail();
//        }catch(IllegalStateException e){
//            assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.1234");
//        }
//        assertThrows(NullPointerException.class, () -> memberService.join(member2));
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

        //then
    }

member1과 member2는 같은 이름으로 회원가입을 진행하기 때문에

member2를 저장하려 할 때 에러가 뜰 것입니다.

밑에 2줄을 주석 처리한 후 주석처리한 곳을 실행해도 결과는 동일하게 나옵니다.

 

그리고 각각의 테스트가 끝나고 나서 리셋을 하는 코드를 만듭니다.

저번에 만든 것처럼요.

    @AfterEach //  Test가 하나 끝날때 마다 repository를 비워줌
    public void afterEach() {
        memberRepository.clearStore();
    }

저번하고 코드가 동일하니 어렵지 않을 것입니다.

 

다음은 findMembers 코드를 만들어보겠습니다.

    @Test
    void findMembers() {
        Member member1 = new Member();
        member1.setName("spring1");
        memberService.join(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        memberService.join(member2);

        List<Member> result = memberService.findMembers();
        assertThat(result.size()).isEqualTo(2);
    }

이렇게 해서 코드를 돌려보면 오류가 뜨지 않는 것을 발견할 수 있습니다.

만들고 나니 저번에 만든 findAll()과 다를 것이 거의 없군요.

그래도 한번씩 해보는 것은 경험삼아 좋습니다.

 

다음은 findOne 코드를 만들어 보겠습니다.

    @Test
    void findOne() {
        Member member1 = new Member();
        member1.setName("spring1");
        memberService.join(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        memberService.join(member2);

        Optional<Member> result = memberService.findOne(1L);
        System.out.println(result.get().getName());
    }

테스트를 돌려보면

제대로 나오는 것을 확인할 수 있습니다.

 

전체 코드를 보겠습니다.

class MemberServiceTest {

    MemberService memberService = new MemberService();
    MemoryMemberRepository memberRepository = new MemoryMemberRepository();

    @AfterEach //  Test가 하나 끝날때 마다 repository를 비워줌
    public void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    //void 회원가입() -> 이렇게 한글로 적어도 된다.
    void join() {
        //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);
//        try{
//            memberService.join(member2);
//            fail();
//        }catch(IllegalStateException e){
//            assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.1234");
//        }
//        assertThrows(NullPointerException.class, () -> memberService.join(member2));
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

        //then
    }

    @Test
    void findMembers() {
        Member member1 = new Member();
        member1.setName("spring1");
        memberService.join(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        memberService.join(member2);

        List<Member> result = memberService.findMembers();
        assertThat(result.size()).isEqualTo(2);
    }

    @Test
    void findOne() {
        Member member1 = new Member();
        member1.setName("spring1");
        memberService.join(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        memberService.join(member2);

        Optional<Member> result = memberService.findOne(1L);
        System.out.println(result.get().getName());
    }
}

 

전체 코드를 실행시켜 보아도 정상적으로 돌아가는 것을 알 수 있습니다.

 

여기서 하나만 더 짚고 넘어가 보죠.

MemberServiceTest 클래스입니다.

이제 MemberService 클래스로 가보겠습니다.

그리고 MemoryMemberRepository클래스 입니다.

MemberServiceTest와 MemberService 모두 MemoryMemberRepository를 사용하고 있습니다.

현재는 문제가 되고 있지는 않지만, 나중에 되서는 문제가 될 수 도 있습니다.

각각 인스턴스를 생성하기 때문에 다른 객체로 인식하기 때문입니다.

store로 정의된 HashMap이 2군데에서 쓰이고 있기 때문이죠.
(이 HashMap은 static으로 정의되어서 이번 케이스에서는 문제가 될 일은 없습니다.)

이 문제를 한 번 해결해보겠습니다.

먼저 MemberService 클래스부터 보겠습니다.

처음에 선언했던 것은 주석처리를 하고

Constructor를 사용해서 정의했습니다.

다음은 MemberServiceTest 입니다.

2개를 선언한 후 

@BeforeEach라는 어노테이션을 사용해서 

테스트가 시작되기 전에 항상 실행하는 코드를 만듭니다.

그리고 실행을 해본다면 오류없이 실행이 잘 되는 것을 확인하실 수 있을 것입니다.

 

MemoryMemberRepository를 먼저 선언한 후

MemberService에서 MemoryMemberRepository 인스턴스를 사용합니다.

이렇게 하면 다른 객체로 인식할 일이 없습니다.

 

MemberService 입장에서 본다면

MemberService를 사용하기 위해서는 MemoryMemberRepository가 항상 필요합니다.

이런 것을 Dependency Injection이라고 합니다.

 

예전에 Spring 관련 글을 쓰면서 DI에 대해 설명한 글이 있었는데

이런 식으로 다시 한번 복습하게 되네요.

좋은 것 같습니다.

 

 

이렇게 테스트 코드를 마치겠습니다.

'Spring' 카테고리의 다른 글

Spring - DI(2)  (0) 2020.09.26
Spring - DI(의존성 주입)  (0) 2020.09.20
Spring - Test Code 작성  (0) 2020.09.12
Spring - static  (0) 2020.09.06
Spring - textarea 관련  (0) 2020.06.23