쌓고 쌓다

회원 서비스 테스트 ( 의존성 주입, 예외 테스트 코드 ) 본문

프로그래밍/spring

회원 서비스 테스트 ( 의존성 주입, 예외 테스트 코드 )

승민아 2023. 5. 7. 00:21

테스트 코드 작성 시 보통 given when then으로 나누어 생각하면 편하다.

given - 이러한(어떤) 상황이 주어졌을 때

when - 이것을 실행했을 때

then - 결과는 이것이 되어야 한다.

 

join 테스트 코드

class MemberServiceTest {

    MemberService memberService = new MemberService();

    @Test
    void join() {
        //given
        Member member = new Member();
        member.setName("member1");

        //when
        Long saveId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
    }

}

저장한 멤버와 memberService에서 꺼낸 member가 일치하는지 테스트 코드를 작성했다.

사실 join 메소드의 테스트는 리포지토리에 잘 들어갔는지도 중요하지만. 예외가 잘 터지는지 또한 테스트 코드를 작성해봐야 한다.

 

join 예외 처리 테스트 코드

(1) try catch

class MemberServiceTest {

    MemberService memberService = new MemberService();

    @Test
    void join_exception() {
        //given
        Member member1 = new Member();
        member1.setName("LSM");

        Member member2 = new Member();
        member2.setName("LSM");

        memberService.join(member1);
        try {
            memberService.join(member2); // Name 중복으로 예외가 터져야함.
            fail();
        } catch (IllegalStateException e) {
            Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
        }
        //when

        //then

    }
}

member1과 member2의 이름은 LSM으로 동일하다. 

member1을 join한 후

member2를 join하려면 이름 중복으로 예외가 터져야 한다.

이때 member2를 조인한 후 예외가 터지기에 다음줄로 넘어가지 않아야 한다.

그래서 member2 조인 다음줄에 fail() 메소드를 실행하여 테스트가 실패했음을 알 수 있다.

 

예외가 터져 catch문으로 넘어갔다면 예외 객체의 getMessage() 메소드를 통해 에러 메시지를 가져오고

이것이 의도한 예외 메시지랑 일치하는지 확인한다.

 

(2) assertThrows

class MemberServiceTest {

    MemberService memberService = new MemberService();

    @Test
    void join_exception() {
        //given
        Member member1 = new Member();
        member1.setName("LSM");

        Member member2 = new Member();
        member2.setName("LSM");

        memberService.join(member1);
        assertThrows(IllegalStateException.class, () -> memberService.join(member2));

    }

}

assertThrows는 아래의 import static에서 사용한다. 

import static org.junit.jupiter.api.Assertions.*;

예외와 어떤 메소드를 실행시켰을 때 터질지를 통해 테스트 코드를 작성할 수 있다.

의도하지 않은 다른 예외 클래스를 사용하여 테스트 코드를 돌려보면 실패한다.

 

assertThrows는 예외를 반환하기에 에러 메시지 확인 또한 가능하다.

IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

 

memoryRepository clear ( )

메소들을 테스트할 때 memoryRepository에 데이터가 누적되므로. 즉, DB를 비워줄 필요가 있다.

MemberService memberService = new MemberService();

/* MemberService 클래스
public class MemberService {
    private final MemberRepository memberRepository= new MemoryMemberRepository();
    ...
}
*/

현재 memberService 내에 리포지토리는 private라

테스트 클래스에서 clear 시켜줄 수 없다.

 

아래와 같이 DB 객체를 생성하여 매번 clearStore() 메소드를 실행시킨다.

class MemberServiceTest {

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


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

 

어? memberService의 DB와 memberRepository의 DB는 다른 것이 아닌가? ( 클래스 변수(static) )

MemberService 또한 MemberRepository의 클래스를 통해 DB를 갖는다.

 

MemoryRepository 클래스의 DB는 static 변수로써 되어 있기에 클래스를 통해 store는 하나로 공유되고 있다.

MemberService의 DB와 MemoryRepository의 DB는 하나로 공유되고 있다.

그래서 MemberRepository 클래스를 통해 clear 해주어도 MemberService가 사용하는 DB와 동일하기에 클리어가 된다.

 

의존성 주입 : DI (Dependency Injection)

MemberService 클래스의 MemoryMemberRepository

 

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

MemberService 클래스가 생성하는 MemoryMemberRepository와

memberRepository 객체가 생성하는 MemoryMemberRepository는 다른 객체이다.

 

적절치 않아 보인다. 또한 MemoryMemberRepository를 두 개 쓸 이유가 없다. 다른 인스턴스인 게 문제다.

같은 MemoryMemberRepository를 사용하는 게 적절해 보인다.

 

같은 MemoryMemberRepository를 사용하게. 즉, 같은 인스턴스를 사용하게 바꿔보자!

 

MemberSerive 클래스 수정 ( DI )

public class MemberService {
    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

MemberRepository를 외부에서 넣어줘서 생성하게 작성했다.


아래와 같이 DB를 생성하여 서비스에 넣어주는 것이다.

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

 

테스트 코드를 아래와 같이 재작성할 수 있다.

class MemberServiceTest {

    MemoryMemberRepository memberRepository;
    MemberService memberService;

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

 

MemberSerivce 입장에서 직접 new 하지 않고 외부에서 넣어주는걸 DI라고 한다.

자세한 건 다음 학습 내용이다...!

 

Comments