본문 바로가기
Back-End/Spring Boot

예습) 스프링 기초

by newny 2023. 4. 27.
반응형

@AfterEach

@AfterEach를 사용하면 각 테스트가 종료될 때 마다 이 기능을 실행한다.
 

@BeforeEach

각 테스트 실행 전에 호출됨
테스트가 서로 영향이 없도록 항상 새로운 객체를 생성하고, 의존관계도 새로 맺어줌
 

Optional<T>

NullPointerException을 방지할 수 있음
null이 올 수 있는 T타입의 객체의 값을 감싸는 Wrapper클래스
복잡한 조건문 없이도 null값으로 인해 발생하는 예외처리를 할 수 있음
of() 메소드나 ofNullable() 메소드를 사용하여 객체를 생성할 수 있음
명시된 값이 null이면 비어있는 Optional객체를 반환함
 

Streams의 findAny()메소드와 findFirst()메소드의 차이

findFirst() : 병렬 처리시 가장 먼저 찾은 선입된 요소 한개를 Optional에 담아서 리턴
findAny() : 병렬 처리시 가장 먼저 찾은 후입된 요소 한개를 Optional에 담아서 리턴
 

ifPresent()

Optional 객체의 값이 null인지 여부를 판단해 줌
null이 아니라면(true) 인자로 받은 람다식을 적용해줌
null이라면(false) 람다식이 실행되지 않음
 

assertThat().isEqualTo 메소드 사용법

assertThat().isEqualTo()의 형태로 기본타입 또는 참조타입의 두 값을 비교할 때 사용 * 두 값이 같지 않을경우 test실패 상태를 반환함
 

assertEquals()메소드

예상값과 실제값을 비교하는데 사용되는 메소드
assertEquals(예상값, 실제값)
두 값이 다르면 테스트 실패 예외를 발생시킴
비교되는 매개값의 타입은 제한없이 모두 가능
 

assertThrows()메소드

두 번째 인자인 람다식을 실행하여 첫 번째 인자인 예상되는 예외와 같은지, 혹은 캐스팅이 가능한 상속 관계의 예외인지 검사함
예상되는 예외가 발생하면 테스트에 성공함
 

IllegalStateException

특정 메소드가 허용되지 않는 상태에서 호출될 때 발생
보통은 객체의 상태가 메소드를 호출하기에 적합하지 않은 경우에 발생

 

[코드 뜯기]

package hello.hellospring.domain;

import hello.hellospring.reepository.MemberRepository;

public class Member { //자바 빈 규약

    //멤버 변수
    private Long id; //아이디
    private String name; //이름

    public Member() {}

    //getter, setter
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

package hello.hellospring.reepository;

import hello.hellospring.domain.Member;

import java.util.List;
import java.util.Optional;

// DB가 정해지지 않았다는 가정하에 임시 저장소에 관련된 인터페이스
public interface MemberRepository {
    Member save(Member member); //아이디, 이름 저장 메소드
    Optional<Member> findById(Long id); //아이디 찾기 메소드
    Optional<Member> findByName(String name); //이름 찾기 메소드
    List<Member> findAll(); //리스트 조회 메소드
}

 

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import java.util.*;

// MemberRepository 인터페이스 구현 객체
public class MemoryMemberRepository implements MemberRepository {
    /** Member 객체 저장소
     * <아이디(key), 멤버객체(value)>순으로 저장
     */
    private static Map<Long, Member> store = new HashMap<>();

    /** 아이디를 생성해주는 sequence
     */
    private static long sequence = 0L;

    /** 아이디 저장 메소드
     * @param member
     * @return Member member
     */
    @Override
    public Member save(Member member) {
        member.setId(++sequence); //시퀀스로 번호 생성
        store.put(member.getId(), member); //store 변수에 아이디, 멤버객체 입력
        return member;
    }

    /**아이디 찾기 메소드
     * @param id
     * @return Member member
     */
    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id)); //null이 발생될 수 있기 때문에 Optional에 담아서 리턴함
    }

    /**Memeber 객체들 전체 찾기 메소드
     * @return ArrayList&lt;member&gt;
     */
    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values()); //store의 값(Member 객체)들을 ArrayList에 담아서 리턴함
    }

    /**이름 찾기 메소드
     * @param name
     * @return Member member
     */
    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream() //Streams를 이용한 Map 컬렉션 제어
                .filter(member -> member.getName().equals(name)) //filter()메소드를 이용하여 매개변수의 name과 같은 값을 가진 member객체 filter함
                .findAny(); //제일 처음 찾은 객체를 findAny 특성상 Optional에 담아서 반환 (null이 발생될 수 있기 때문에 Optional에 담아서 리턴함)
    }

    /** store 변수 내부의 키와 값들을 전체 삭제하는 메소드
     */
    public void clearStore() {
        store.clear();
    }
}

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {

    // 기존에는 회원 서비스가 메모리 회원 리포지토리를 직접 생성하게 함
    // private final MemberRepository memberRepository = new MemoryMemberRepository();

    // 외부에서 생성된 MemberRepository 객체를 매개값으로 받음으로서
    // MemberService 객체 생성됨 (의존성 주입)
    private final MemberRepository memberRepository;

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

    /**
     * 회원가입
     */
    public Long join(Member member) {
        //중복 회원 검증
        validateDuplicateMember(member);

        //memberRepository 객체의 store저장소에 save() 메소드 사용하여 회원정보 저장
        memberRepository.save(member);

        //sequence로 생성된 아이디 리턴
        return member.getId();
    }

    /**
     * 중복 회원 검증
     * @param member
     */
    private void validateDuplicateMember(Member member) {
        // memberRepository 객체의 findByName()메소드를 이용하여 Optional에 담은 Member객체 반환
        memberRepository.findByName(member.getName())
                //Optional<T>의 ifPresent()메소드를 이용하여 결과가 true이면 람다식을 적용함
                .ifPresent(m -> {
                    //예외 생성하여 메소드를 호출한 곳으로 throw함
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }

    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers() {
        // memberRepository 객체의 findAll() 메소드로 회원 이름 전체 조회
        return memberRepository.findAll();
    }

    /**
     * 회원 한명 조회
     * @param memberId
     * @return
     */
    public Optional<Member> findOne(Long memberId) {
        // memberRepository 객체의 findAll() 메소드로 특정 회원 이름 조회
        return memberRepository.findById(memberId);
    }
}

 

[테스트 코드 뜯기]

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import hello.hellospring.reepository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

// 테스트 코드
public class MemoryMemberRepositoryTest {

    // 저장소 객체 생성
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach // 각테스트들이 종료될 때 마다 메모리DB에 저장된 데이터를 삭제함
    public void afterEach() {
        repository.clearStore();
    }

    // 객체 저장 테스트
    @Test
    public void save() {
        //Member객체 생성
        Member member = new Member();

        //Member객체에 이름 저장
        member.setName("spring");

        //MemoryMemberRepository 클래스의 save()메소드를 이용하여 Member객체 저장하면서
        //아이디는 시퀀스로 저장
        repository.save(member);

        //MemoryMemberRepository의 findById()메소드에 매개값으로 id를 넣고
        //Optional 객체의 get()메소드를 이용하여 id 추출함
        //해당 id가 key값인 객체를 store 변수(HashMap)에서 가져옴
        Member result = repository.findById(member.getId()).get();

        //찾은 객체와 위의 저장된 객체가 일치 하는지 확인
        assertThat(result).isEqualTo(member);
    }

    //이름찾기 테스트
    @Test
    public void findByName() {
        Member member1 = new Member(); //member1 객체 생성
        member1.setName("spring1"); //member1 객체에 이름 저장
        repository.save(member1); //member1 객체 저장소에 저장

        Member member2 = new Member(); //member1 객체 생성
        member2.setName("spring2"); //member1 객체에 이름 저장
        repository.save(member2); //member1 객체 저장소에 저장

        // MemoryMemberRepository의 findByName()메소드에 매개값으로 위의 저장 Member객체들 중
        // 한 객체의 이름 넣고, store(HashMap)의 값들중 stream의 fiter()메소드를 이용하여
        // findAny()메소드를 이용하여 가장 먼저 찾은 요소 한개를 Optional에 담아서 리턴함
        Member result = repository.findByName("spring1").get();

        // 찾은 객체와 위의 저장된 객체가 일치 하는지 확인
        assertThat(result).isEqualTo(member1);
    }

    //회원 리스트 조회 테스트
    @Test
    public void findAll() {
        Member member1 = new Member(); //member1 객체 생성
        member1.setName("spring1"); //member1 객체에 이름 저장
        repository.save(member1); //member1 객체 저장소에 저장

        Member member2 = new Member(); //member1 객체 생성
        member2.setName("spring2"); //member1 객체에 이름 저장
        repository.save(member2); //member1 객체 저장소에 저장

        // MemoryMemberRepository의 findAll()메소드를 이용하여
        // store의 값들만 ArrayList<member>()에 저장하고 리턴함 (HashMap은 순서가 없기 때문에 무작위로 저장됨)
        List<Member> result = repository.findAll();

        // 위 두 객체가 저장되어있는지 ArrayList의 size확인
        assertThat(result.size()).isEqualTo(2);
    }

}

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

// 서비스 테스트
public class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    //테스트 실행 전 MemberService 클래스의 구조가 의존성 주입이 되었기 때문에 객체를 생성하여 주입함
    @BeforeEach
    public void beforeEach() {
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

    //테스트 실행 후 저장소의 내용을 지워줌
    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

    //회원가입 메소드
    public void 회원가입() throws Exception { //발생하는 Exception을 throws함
        Member member = new Member(); //member 객체 생성
        member.setName("hello"); //member 객체의 멤버변수 이름을 hello라고 저장

        //member객체 중복회원검증 후 저장소에 저장, 아이디 리턴함
        Long saveId = memberService.join(member);

        //리턴한 아이디로 저장소에서 해당객체를 찾음
        Member findMember = memberRepository.findById(saveId).get();

        //저장소에서 찾은 객체와 새로 저장한 객체의 이름이 일치하는지 확인
        assertEquals(member.getName(), findMember.getName());
    }

    @Test
    public void 중복_회원_예외() throws Exception {
        //member1 객체 생성 후 이름 저장
        Member member1 = new Member();
        member1.setName("spring");

        //member2 객체 생성 후 이름 저장(Exception발생을 위해 member1과 똑같게 저장함)
        Member member2 = new Member();
        member2.setName("spring");

        //member1만 저장소에 저장함
        memberService.join(member1);

        //예상되는 예외(IllegalStateException)와 두번째 매개인 람다식을 실행했을 때의 예외가
        //같은지 검사함
        //예외가 같다면 테스트 계속 진행됨
        IllegalStateException e = assertThrows(IllegalStateException.class,
                () -> memberService.join(member2));

        //예외의 메세지가 같은지 확인함
        assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");

    }
}
반응형

댓글