일단은 개발해야하니까 메모리에 넣었다 뺐다 할수있는 단순한 구현체를 만들 것이다. 나중에 데이터 저장소가 정해지면 바꿔끼워야 하기 때문에 인터페이스가 필요하다.
위와 같이 Member, MemberRepository, MemoryMemberRepository를 생성한다.
// Member.java
package hello.hellospring.domain;
public class Member {
private Long id;
private String name;
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;
}
}
// MemberRepository.java
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member);
Optional<Member> findById(Long id);
Optional<Member> findByName(String name);
List<Member> findAll();
}
//MemoryMemberRepository.java
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
위에 작성한 코드가 정상적으로 동작하는지 검증하는 법이 테스트 케이스를 작성하는 것이다.
보통 테스트할 때 main 메서드를 실행하거나 웹 어플리케이션의 컨트롤러를 통해 해당 기능을 실행하는데, 이러한 방법은 준비하고 실행하는데 오래걸리고 반복 실행하기 어렵고 테스트를 한번에 실행하기 어렵다는 단점이 있다.
자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다
먼저 다음과 같은 경로에 Test 클래스를 만들어준다.
테스트 클래스를 만들 땐, 보통 테스트할 클래스 옆에 Test를 붙이고 똑같은 경로에 만들어주는게 관례다.
package hello.hellospring.repository;
import org.junit.jupiter.api.Test;
class MemoryMemberRepositoryTest {
MemberRepository repository = new MemoryMemberRepository();
@Test
public void save() {
}
}
다음과 같이 작성하고 save()를 실행해보면 무언가 실행이 된다.
@Test
public void save() {
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
System.out.println("result = " +( result == member));
}
이런식으로 콘솔에 테스트를 출려해볼 수도 있는데, assertions라는 기능이있다.
println()을 지우고 다음과 같은 코드를 넣어보자
Assertions.assertEquals(member, result);
아까와는 다르지만 테스트가 패스했다는 게 뜬다. 그런데 result대신에 뭐..아무거나 넣어본다면?
Assertions.assertEquals(member, null);
Expected :hello.hellospring.domain.Member@32cf48b7
Actual :null
위와 같이 뜬다.
또 다른 방법은 똑같은 Assertions 인데 org.assertj.core.api에 있는 것이 있다.
Assertions.assertThat(member).isEqualTo(result);
실무에서는 빌드툴이랑 엮어서 빌드할떄 테스트케이스에서 통과하지못하면 다음단계로 못넘아고도록 막는다.
Junit 테스트케이스에 대해 배워보기
// 전체코드
// MemoryMemberRepositoryTest.java
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
class MemoryMemberRepositoryTest {
MemberRepository repository = new MemoryMemberRepository();
@Test
public void save() {
Member member = new Member();
member.setName("spring");
repository.save(member);
Member result = repository.findById(member.getId()).get();
Assertions.assertThat(member).isEqualTo(result);
}
@Test
public void findByName() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
Member result = repository.findByName("spring1").get();
Assertions.assertThat(result).isEqualTo(member1);
}
@Test
public void finaAll() {
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring1");
repository.save(member2);
List<Member> result = repository.findAll();
Assertions.assertThat(result.size()).isEqualTo(2);
}
}
근데 위에 코드를 전체 테스트케이스를 돌려보면 중간에 잘되던 findByName이 실패했다는 것을 볼 수 있다. 변수선언할때 순서가 잘못되서 그렇다 < 이해못함
그래서 각 메소드가 실행되고 난 뒤에 모드 클리어를 해줘야한다
MemoryMemberRepository
에서 만들었던 clearStore()
를 불러준다.
@AfterEach
public void afterEact() {
repository.clearStore();
}
AfterEach
는 각 메소드가 실행되고 실행되어질 메소드를 실행해준다
테스트케이스를 작성할땐 서로 의존관계없이 설계되어야하는데 그러기위해서는 하나의 메소드가 끝날때마다 모든 데이터를 깔끔히 지워주는 어떤 장치가 필요하다.
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;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/**
* 회원가입
*/
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();
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
package hello.hellospring.service;
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.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
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("hello");
//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 e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
// memberService.join(member1);
// try {
// memberService.join(member2);
// fail();
// } catch (IllegalStateException e) {
// Assertions.assertThat(e.getMessage().isEqualTo("이미 존재하는 회원입니다."));
// }
//then
}
@Test
void findMembers() {
}
@Test
void findOne() {
}
}