서비스와 서비스 테스트, DI

OneTwoThree·2023년 6월 18일
0

회원 Service 작성

  • 회원 서비스 : 리포지토리랑 도메인을 활용해 실제 비즈니스 로직을 작성함
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();

    //회원 가입
    public Long join(Member member){
        //이름이 중복되는 회원이 없어야함
        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();
    }

	//회원 id로 1명 조회 
    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }

}
  • validateDuplicateMember 메소드는 원래 join 안에서 이름 중복을 검사하려고 만든 코드를 드래그 -> 우클릭 -> refactor -> extract method로 메소드를 추출한 것임. 이런 방식으로 리팩토링 가능

  • 서비스는 리포지토리를 멤버로 갖고 비즈니스 로직을 구현함

회원 Service 테스트

  • 클래스명 드래그 하고 ctrl+shitf+t 누르면 이렇게 테스트 생성할 수 있음
  • 테스트코드는 메소드명을 한국어로 짓는 경우도 많음
package hello.hellospring.service;

import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

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

class MemberServiceTest {

    MemberService memberService =   new MemberService();

    @Test
    void 회원가입() {
        // given 주어진 것
        Member member = new Member();
        member.setName("hello");

        // when 실행한 것
        Long saveId = memberService.join(member);

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

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}
  • given, when, then 문법 사용
    @Test
    public void 중복_회원_예외(){
        //given
        Member member1 = new Member();
        member1.setName("spring");
        Member member2 = new Member();
        member2.setName("spring");

        //when
        memberService.join(member1);
        //람다식의 로직을 실행할 때 IllegalStateException이 발생해야 함
        //이렇게 작성하면 try-catch 형식으로 쓰지 않아도 됨
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
    }
  • assertThrows로 예외가 발생하는 경우를 테스트 할 수 있음
  • 반환값으로 예외를 반환함
  • 반환한 예외로 예외메세지를 assertThat으로 비교할 수 있음

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.repository.MemoryMemberRepositoryTest;
import org.assertj.core.api.AssertJProxySetup;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

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

class MemberServiceTest {

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

    @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();
        Assertions.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이 발생해야 함
        //이렇게 작성하면 try-catch 형식으로 쓰지 않아도 됨
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
    }

    @Test
    void findMembers() {

    }

    @Test
    void findOne() {
    }
}
  • 리포지토리를 테스트 할 때와 마찬가지로 클래스 단위로 테스트 할 때는 리포지토리를 비워줘야함 (@AfterEach 사용)

DI

문제점?

  • MemberService에서 사용하는 new로 생성한memberMemoryRepository랑, MemberServiceTest에서 사용하는 new로 생성한 memberMemoryRepository는 다른 객체가 됨
  • 같은 repository로 테스트 하는게 맞는건데.. 다른 repsitory로 테스트 하고 있는 상황
public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
  • 먼저 MemberService에서 리포지토리를 직접 new로 생성하는 것이 아닌 외부에서 넣어주도록 바꿈
class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }
  • 테스크 클래스에서는 다음과 같이 @BeforeEach 를 활용해서 각 메소드 실행 전 마다 서비스와 리포지토리를 생성하도록 함
  • 테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고 의존관계도 새롭게 맺어줌
  • Member Service 입장에서 DI (외부에서 객체를 넣어줌)

0개의 댓글