TDD 멤버십 등록 API 구현 2

윤현우·2023년 2월 5일
0

TDD공부

목록 보기
4/7
post-thumbnail

[멤버십 등록 API 구현]

다음으로 멤버십을 등록할 테스트를 작성해 보았다.

@Test
    void 멤버십등록() {
        //given
        Membership member = Membership.builder()
                .userId("userA")
                .membershipName("네이버")
                .point(10000)
                .build();

        //when

        // 실제로는 dto로 받아와서 엔티티에 값을 넣어준다. 하지만 테스트 코드 작성이므로 바로 엔티티에 저장후 값을 확인한다.
        Membership result = membershipRepository.save(member);

        //then
        assertThat(result.getId()).isNotNull();
        assertThat(result.getUserId()).isEqualTo(member.getUserId());
        assertThat(result.getMembershipName()).isEqualTo(member.getMembershipName());
        assertThat(result.getPoint()).isEqualTo(member.getPoint());
    }

Membership 엔티티 클래스에 해당 컬럼들이 없기 때문에 컴파일 오류가 난다. 컬럼을 생성하고 다시 테스트를 돌려보자

public class Membership {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // id 자동증가 생성
    private Long id;

    @Column
    @NotNull
    private String userId;

    @Column
    @NotNull
    private String membershipName;

    @Column
    private int point;

    @CreationTimestamp
    @Column(nullable = false, length = 20, updatable = false)
    private LocalDateTime createdAt;

    @UpdateTimestamp
    @Column(length = 20)
    private LocalDateTime updatedAt;
}

이후 테스트를 다시 돌려보면 성공하는 것을 볼 수 있다.

리팩토링 단계로 멤버십타입을 enum형식으로 바꾸면 관리하기 좋을 것 같다.

	@Test
    void 멤버십등록() {
        //given
        Membership member = Membership.builder()
                .userId("userA")
                .membershipName(MembershipType.NAVER)
                .point(10000)
                .build();

        //when

        // 실제로는 dto로 받아와서 엔티티에 값을 넣어준다. 하지만 테스트 코드 작성이므로 바로 엔티티에 저장후 값을 확인한다.
        Membership result = membershipRepository.save(member);

        //then
        assertThat(result.getId()).isNotNull();
        assertThat(result.getUserId()).isEqualTo(member.getUserId());
        assertThat(result.getMembershipName()).isEqualTo(member.getMembershipName());
        assertThat(result.getPoint()).isEqualTo(member.getPoint());
    }

enum형식이 없으므로 컴파일 오류가 난다. 매우 잘한 것이다.

테스트 코드를 먼저 변경하고 그 이후에 프로덕트 코드를 변경하는 단계를 잘 생각해서 해보자.

컴파일 오류를 확인 했으니, MembershipType enum클래스를 만들어보자.

@Getter
@RequiredArgsConstructor
public enum MembershipType {

    NAVER("네이버"),
    LINE("라인"),
    KAKAO("카카오"),
    ;

    private final String companyName;

}

테스트를 돌려보면 아직 컴파일 오류가 난다.

Membership 클래스의 membershipType을 String 타입으로 해놨기 때문에 오류가 난다.

String을 MembershipType타입으로 변경하고 다시 테스트를 돌려보자

성공하였다!!

다음으로는 어떤 사용자가 중복으로 같은 멤버십을 등록할 시 중복이 안되게 등록하지 못하게 하는 것이다.

일단 레파지토리에서 userId와 MembershipType으로 해당 엔티티를 찾는 테스트 코드부터 작성해보자.(그래야 서비스 부분에서 중복 검사시 필요한 데이터를 찾을 수 있기 때문이다.){

	@Test
    void 사용자가이미등록한멤버십일때_중복검사(){
        //given
        Membership member = Membership.builder()
                .userId("userA")
                .membershipName(MembershipType.NAVER)
                .point(10000)
                .build();

        //when
        Membership result = membershipRepository.save(member);
        Membership findresult = membershipRepository.findByUserIdAndMembershipType("userA", MembershipType.NAVER);
        
        //then
        assertThat(findresult.getId()).isNotNull();
        assertThat(findresult.getUserId()).isEqualTo(result.getUserId());
        assertThat(findresult.getMembershipName()).isEqualTo(result.getMembershipName());
        assertThat(findresult.getPoint()).isEqualTo(result.getPoint());
    }

레파지토리에 findByUserIdAndMembershipTyp이 존재하지 않아 컴파일 오류가 난다.

레파지토리에 추가해주자.

public interface MembershipRepository extends JpaRepository<Membership, Long> {
    Membership findByUserIdAndMembershipType(String userId, MembershipType membershipType);

}

다시 실행해보면 정상적으로 findByUserIdAndMembershipType이 잘 작동 되며 테스트에 성공하는 것을 볼 수 있다.

야호!

다음으로 Service부분을 테스트 할 차례이다.

처음으로 멤버십 등록을 할 때 중복 검사를 실행시켜 중복이 있을 시 생성을 하지 않는 테스트 코드를 작성해본다.

	@Test
    void 멤버십서비스에서중복검사(){
        //given
        
        //제일 먼저 멤버십이 있다는 가정을 한다.
        Membership member = Membership.builder()
                .userId("userA")
                .membershipType(MembershipType.NAVER)
                .point(10000)
                .build();
        
        Membership userA = membershipRepository.save(member);
        
        //when
        String userId = "userA";
        MembershipType membershipType = MembershipType.NAVER;
        Integer point = 10000;
        
        Membership result = memberService.create(userId, membershipType, point);
        
        //then
        assertThat(result.getId()).isNotNull();
        assertThat(result.getUserId()).isEqualTo(userA.getUserId());
        assertThat(result.getMembershipType()).isEqualTo(userA.getMembershipType());
        
    }

제일 처음 해당 멤버십을 가지고 있으면 있다고 말해준뒤 등록할 멤버십이 원래 있는 멤버십이 같은지 테스트하는 코드를 작성했다.

같은지 테스트를 한 후 리팩토링을 할 것이다.

제일 먼저 memberService가 없기 때문에 프로덕트 코드에 MemberService를 추가해준 후, create메서드를 작성한다.

public Membership create(String userId, MembershipType membershipType, Integer point) {
        Membership result = membershipRepository.findByUserIdAndMembershipType(userId, membershipType);

        if(result != null) {
            System.out.println("이미 등록된 회원멤버십입니다.");

            Membership member = Membership.builder()
                    .userId(userId)
                    .membershipType(membershipType)
                    .point(point)
                    .build();

            Membership makeMembership = membershipRepository.save(member);

            return makeMembership;
        } else {
            Membership member = Membership.builder()
                    .userId(userId)
                    .membershipType(membershipType)
                    .point(point)
                    .build();

            Membership makeMembership2 = membershipRepository.save(member);

            return makeMembership2;
        }
}

현재 create 메서드는 테스트 확인을 위해 작성하였다. 테스트에 성공하면 리팩토링을 통해 다시 재작성한다.

테스트를 돌려보면 result != null가 참이기 때문에

"이미 등록된 회원멤버십입니다."라는 출력을 하며

id값이 2인 엔티티가 생성된다.

MembershipService에서 컨트롤러로 반환하는 객체는 Membership 엔티티이다. 엔티티를 반환하는 것 보다는 DTO를 반환하는 것이 바람직하므로 DTO를 반환하도록 리팩토링하도록 하자.

public class MembershipDto {

    private Long id;
    private String userId;
    private MembershipType membershipType;
    private int point;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;


    public Membership toEntity(){
        Membership membership = Membership.builder()
                .id(id)
                .userId(userId)
                .membershipType(membershipType)
                .point(point)
                .createdAt(createdAt)
                .updatedAt(updatedAt)
                .build();

        return membership;
    }
}

Membership 엔티티 클래스에도 toDto()메서드를 작성하여 만들어둔다.(빌더 사용)

public Membership create(String userId, MembershipType membershipType, Integer point    /*나중에 MembershipDto로 변경*/) {
        Membership result = membershipRepository.findByUserIdAndMembershipType(userId, membershipType);

        if(result != null) {
            System.out.println("이미 등록된 회원멤버십입니다.");

            return null;
        } else {
            // MembershipDto로 받은 데이터 toEntity()메서드를 사용하여 엔티티로 변경
            Membership member = Membership.builder()
                    .userId(userId)
                    .membershipType(membershipType)
                    .point(point)
                    .build();

            Membership makeMembership2 = membershipRepository.save(member);

            return makeMembership2;
        }
    }

현재 테스트 코드 작성을 기반으로 아직 컨트롤러 부분의 프로덕트 코드가 작성되지 않았기 때문에 서비스 부분의 프로덕트 코드 중, 리팩토리해야 할 부분을 주석처리 함으로써 쉽게 수정할 수 있게 만든다.

컨트롤러 부분의 테스트는 MockMvc를 사용하여 진행해야 하는데 아직 배우지 않아 요번 공부에서 컨트롤러 테스트는 뺐다.

profile
개발자가 되는 그날까지

0개의 댓글