회원 관리에 관한 비즈니스 모델을 만들려고 한다.
웹 MVC구조를 사용하여 개발을 하는데 구조는 다음과 같다,
Service: 핵심 비즈니스 로직 (회원 가입, 회원조회(Id, Name등))
Repository: DB에 접근, Domain 객체를 저장하고 관리
Domain: Member, Order 등 DB에 저장하고 관리
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();
}
}
아직 DB를 만들지 않아 repository 클래스에서 HashMap<Long, Member> store에 저장하였다.
findByName() 메서드에서 store에 저장된 Member의 이름을 찾아 반환해주는 람다식인데, 람다식은
여전히 어렵다...
Optional을 사용하였는데, Null을 Optional에 포장하게 되면 Null을 값으로 보고 로직을 구현할 수 있다.
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m ->{
throw new IllegalStateException("이미 존재하는 회원");
});
}
회원가입을 할 때 회원 중복을 검증 메서드 구현. (마찬가지로 람다식이라 어렵다...)
개발자들의 하루 일과는 70%이 테스트케이스 작성이라고 한다. 그만큼 공을 들여야 하는 작업이라고 한다.
@Test
public void Validation_name() {
Member member1 = new Member();
member1.setName("A");
Member member2 = new Member();
member2.setName("A");
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () ->
memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원");
}
member1과 같은 이름의 member2가 가입했을 때 중복된 이름일 경우 에러 메세지가 출력되고
asserThat().isEqualTo로 중복오류인지 검증.
Service에 포함된 메서드로 테스트를 하는 중, Repository를 생성할 경우 같은 메모리가 아닐 수 있다.(다른 인스턴스일 수 있기에 내부 값이 달라질 수 있다.)
//Service Class
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
//생성자 선언 시, MemberService 입장에서는 새롭게 new()를 하지 않고
//외부에서 memberRepository를 넣어준다. 의존성 주입으로 같은 메모리로 할당할 수 있다.
//Test Class
@BeforeEach
void BeforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
다양한 케이스를 테스트하면 DB에 값이 누적되어 정상적으로 테스트가 불가능할 수가 있다.
따라서 @AfterEach로 테스트가 끝날 때마다 저장소를 초기화시켜준다. (store.clear();)