[Spring boot] 회원 도메인 만들기 & 테스트 케이스 작성하기

Jiwoo An·2022년 10월 17일
0

Java Spring

목록 보기
2/2

계층 구조를 이쁘게 만들어보자!

0. 웹 애플리케이션 계층 구조

컨트롤러 : 웹 MVC의 컨트롤러 역할
서비스 : 핵심 비즈니스 로직
리포지토리 : DB에 접근, 도메인 객체를 DB에 저장하고 관리 ex) findById
도메인 : 비즈니스 도메인 객체 ex) 회원, 주문 등 주로 DB에 저장하고 관리됨

아직 데이터 저장소가 선정되지 않아, 인터페이스를 가지고 구현 클래스를 변경할 수 있도록 설계한다.
초기 개발 단계에서는 가벼운 메모리 기반의 데이터 저장소를 사용한다.


1. 도메인 작성

도메인 작성 전에 domain 폴더를 만들어준다.

도메인은 장고의 모델 비슷한 부분이다.
디비에 이러한 필드가 있을 거다라고 알려준다.
getter와 setter도 만들어준다.

  • demo\src\main\java\com\example\demo\domain\Member.java
package com.example.demo.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;
    }
    public void setNmae(String string) {
    }

}

2. 리포지토리 작성

리포지토리 작성 전에 repository 폴더를 만들어준다.

추상메소드 부분도 필요하다. 아래에서 구현할 것임.

  • demo\src\main\java\com\example\demo\repository\MemberRepository.java
package com.example.demo.repository;

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

import domain.Member;

public interface MemberRepository {
    Member save(Member member);
    Optional<Member> findById(Long id);
    Optional<Member> findByName(String name);
    List<Member> findAll();
}

위에서도 말했듯이 DB를 바꿀 수도 있기 때문에 인터페이스로 작성했다.
Optional는 null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE(NullPointerException)가 발생하지 않도록 도와준다. (Java8부터)

MemoryMemberRepository 는 인터페이스를 상속받아 추상메소드를 구현하는 부분이다.

  • demo\src\main\java\com\example\demo\repository\MemoryMemberRepository.java
package com.example.demo.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import domain.Member;

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());
    }
    
}

save(Member member) 는 id를 하나 올려주고, 멤버를 저장한다.
findById(Long id) 는 파라미터로 넘어온 id를 찾아서 준다.
Optional.ofNullable : null인지 아닌지 확신할 수 없는 객체를 담고 있는 Optional 객체를 생성한다. null이 넘어올 경우, NPE를 던지지 않고 Optional.empty()와 동일하게 비어 있는 Optional 객체를 얻어온다.

3. 테스트 케이스 작성

직접 포스트맨으로 계속 검사할 수는 없으니 java의 JUnit을 활용해 테스트 케이스를 작성한다.

test 폴더 안에 repository 폴더를 만들고 파일을 생성해준다.
MemoryMemberRepository의 테스트니까 MemoryMemberRepositoryTest.java로 해줬다.
넘 길어요.


  • demo\src\test\java\com\example\demo\repository\MemoryMemberRepositoryTest.java
package com.example.demo.repository;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import domain.Member;
import repository.MemoryMemberRepository;

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

import java.util.List;

class MemoryMemberRepositoryTest {
    
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save() {
        Member member = new Member();
        member.setNmae("spring");

        repository.save(member);

        Member result = repository.findById(member.getId()).get();
        assertThat(member).isEqualTo(result);
    }
}

외부에서 사용하지 않으니까 public class 라고 적지 않고, class라고만 했다.

assertEquals(예상 값, 실제 값) == assertThat(실제 값).isEqualTo(예상 값)


왼쪽에 있는 실행 버튼을 누르면 짜잔 잘 된다.


실제값에 null을 넣으니까 틀렸다고 나온다.

빌드툴에서 빌드 시 테스트를 통과하지 못하면 다음 단계로 넘어가지 못한다.

package com.example.demo.repository;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import domain.Member;
import repository.MemoryMemberRepository;

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

import java.util.List;

class MemoryMemberRepositoryTest {
    
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save() {
        Member member = new Member();
        member.setNmae("spring");

        repository.save(member);

        Member result = repository.findById(member.getId()).get();
        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();

        assertThat(result).isEqualTo(member1);
    }

    @Test
    public void findAll() {
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);

        List<Member> result = repository.findAll();

        assertThat(result.size()).isEqualTo(2);
    }

}

나머지 코드도 다 작성해주고, 실행을 해본다.

.size() : 리스트 안에 있는 원소 개수
.length : 배열의 전체 길이

전체 테스트를 실행해보면 오류가 생긴다.
테스트할 때는 순서가 보장되지 않은 채로 실행되는데 전에 저장되었던 친구가 튀어나오기 때문에 오류가 발생했다.

테스트가 끝날 때마다 메모리를 지워주는 코드를 만들어야 한다.
=> 테스트는 의존관계 없이 설정되어야 한다.

메모리를 지워주는 함수를 만들 거니까 메모리레포지토리에 만들어준다.

  • demo\src\main\java\com\example\demo\repository\MemoryMemberRepository.java
package com.example.demo.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import domain.Member;

public class MemoryMemberRepository implements MemberRepository{
   
   (중략)

    public void clearStore() {
        store.clear();   
    }
    
}
  • demo\src\test\java\com\example\demo\repository\MemoryMemberRepositoryTest.java
package com.example.demo.repository;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import domain.Member;
import repository.MemoryMemberRepository;

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

import java.util.List;

class MemoryMemberRepositoryTest {
    
    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach() {
        repository.clearStore();
    }
	
  	(후략)
}

@AfterEach 어노테이션을 이용하면 테스트 클래스의 @Test가 붙은 메서드 다음에 실행된다.

성공!

TDD 따악 기다려.


Reference

0개의 댓글