카테고리 없음

[스프링부트] 실전! 스프링 부트와 JPA 활용1 #4-3 회원 기능 테스트

aSpring 2023. 11. 2. 11:18
728x90
728x90
※ 본 포스팅은 김영한 강사님의 인프런 '실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발' 강의를 들으며 작성한 수강일지 입니다.

 

| 회원 도메인 개발

1. 회원 리포지토리 개발
2. 회원 서비스 개발
3. 회원 기능 테스트

 

3. 회원 기능 테스트

테스트 요구사항

  • 회원가입을 성공해야 한다.
  • 회원가입 할 때 같은 이름이 있으면 예외가 발생한다.

회원가입 테스트 코드

package jpabook.jpashop.service;

import jpabook.jpashop.domain.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import static org.junit.jupiter.api.Assertions.assertEquals;

//import static org.junit.Assert.*;

//@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {

    @Autowired MemberService memberService;
    @Autowired
    jpabook.jpashop.Repository.MemberRepository memberRepository;

    @Test
    public void 회원가입() throws Exception {
        //given 이런게 주어졌을 때
        Member member = new Member();
        member.setName("kim");

        //when 이렇게 하면
        Long savedId = memberService.join(member);

        //then 이렇게 된다. 검증해라
        assertEquals(member, memberRepository.findOne(savedId));
    }

    @Test
    public void 중복_회원_예외() throws Exception {
        //given
        //when
        //then
    }
}

- 테스트 케이스 실행 시 결과를 보면 insert 쿼리가 없는 것을 알 수 있음

JPA에서는

- persist를 한다고 해서 db에 insert문이 나가지 않음(DB 전략마다 다르지만 기본적으로 Generate Value 전략에서는 insert문이 나가지 않음)

- 왜냐? DB transaction이 commit 될 때 flush 되면서 DB insert 쿼리가 나가는 것(이 때 JPA 영속석 컨텐스트에 있는 멤버 객체가 insert) -> Transaction이 중요

- spring Transactional annotation이 테스트 케이스에 있으면 기본적으로 Rollback을 해버림

 

Rollback(false)

- 이제 insert문이 나가는 것을 볼 수 있음

- DB에서도 name kim 데이터 확인 가능함

 

Rollback이지만 insert query 날리는 것을 확인하고 싶다면

- flush : 영속성 컨텍스트에 있는 어떤 변경이나 등록 내용을 DB에 반영하는 것

 

중복 회원 예외 테스트

package jpabook.jpashop.service;

import jpabook.jpashop.domain.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

import static org.assertj.core.api.Fail.fail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

//import static org.junit.Assert.*;

//@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {

    @Autowired MemberService memberService;
    @Autowired
    jpabook.jpashop.Repository.MemberRepository memberRepository;
    @Autowired
    EntityManager em;  // 1) 기본적으로 Rollback 해버리지만 그래도 insert query 날리는 것을 보고싶다면

    @Test
//    @Rollback(false) // db insert 된 것을 보기 위해
    public void 회원가입() throws Exception {
        //given 이런게 주어졌을 때
        Member member = new Member();
        member.setName("kim");

        //when 이렇게 하면
        Long savedId = memberService.join(member);

        //then 이렇게 된다. 검증해라
        em.flush(); // 2) 이렇게 하면 query 보임 -> flush : 영속성 컨텍스트에 있는 어떤 변경이나 등록 내용을 DB에 반영하는 것
        assertEquals(member, memberRepository.findOne(savedId));
    }

//    @Test(expected = IllegalStateException.class) // JUnit5에서는 사용하지 않음
    @Test
    public void 중복_회원_예외() throws Exception {
        //given
        Member member1 = new Member();
        member1.setName("kim");

        Member member2 = new Member();
        member2.setName("kim"); // 같은 이름이므로 예외가 발생해야 해서 밖으로 나가게 되어야 함("이미 존재하는 회원입니다." 이쪽으로)

        //when
        memberService.join(member1);
//        try {
        assertThrows(IllegalStateException.class, () -> memberService.join(member2)); // 예외가 발생해야 한다!!
//        } catch(IllegalStateException e) {
//            return;
//        }

        //then
        fail("예외가 발생해야 한다."); // 밖으로 안나가고 코드가 돌다가 여기에 오면 안됨
    }
}

 

try catch

- 테스트 성공

- 두가지 함께 돌려보기

 

기술 설명

  • @RunWith(SpringRunner.class): 스프링과 테스트 통합
  • @SpringBootTest: 스프링 부트 띄우고 테스트(이게 없으면 @Autowired 다 실패)
  • @Transactional: 반복 가능한 테스트 지원, 각각의 테스트를 실행할 때마다 트랜잭션을 시작하고 테스트가 끝나면 트랜잭션을 강제로 롤백(이 어노테이션이 테스트 케이스에서 사용될 때만 롤백)

기능 설명

  • 회원가입 테스트
  • 중복 회원 예외처리 테스트
참고: 테스트 케이스 작성 고수 되는 바법 - Given, When, Then
(http://martinfowler.com/bliki/GivenWhenThen.html)
이 방법이 필수는 아니지만 이 방법을 기본으로 해서 다양하게 응용하는 것을 권장

 

테스트 케이스를 위한 설정 - 메모리 DB 사용하기

테스트 케이스는 격리된 환경에서 실행하고, 끝나면 데이터를 초기화하는 것이 좋다. 그런 면에서 메모리 DB를 사용하는 것이 가장 이상적이다.

추가로 테스트 케이스를 위한 스프링 환경과, 일반적으로 애플리케이션을 실행하는 환경은 보통 다르므로 설정 파일을 다르게 사용하자.

다음과 같이 간단하게 테스트용 설정 파일을 추가하면 된다.

 

-

- 기본적으로 src에는 main과 test가 있고, test에도 resources 폴더를 만들고 main의 yml 파일을 복사 붙여넣기 한다.

- 테스트 할 때는 main 폴더가 아닌 test 폴더 내 파일들이 우선시 된다

- h2 메모리 모드로 띄우기

https://www.h2database.com/html/main.html > Cheet Sheet > In-Memory

  • test/resources/application.yml

spring:
  datasource:
    url: jdbc:h2:~/test # test는 memory mode로 돌리기
    username: sa
    password:
    driver-class-name: org.h2.Driver # DB connection과 관련된 data source 설정이 완료 됨

  jpa:
    hibernate:
      ddl-auto: create # 자동으로 table을 만들어주는 모드(애플리케이션 실행 시점에 내가 가지고 있는 엔티티 정보를 보고 다 지운 후 다시 생성)
    properties:
      hibernate:
#          show_sql: true # system.out에 출력 -> 운영 환경에서는 system.out에 찍으면 안됨, 전부 log에 찍어야 함
        format_sql: true

logging:
  level:
    org.hibernate.SQL: debug # JPA나 Hibernate가 생성하는 SQL이 모두 보이게 됨 -> log에 출력
    org.hibernate.type: trace

 

이제 테스트에서 스프링을 실행하면 이 위치에 있는 설정 파일을 읽는다.

(만약 이 위치에 없으면 src/resources/application.yml을 읽는다.)

 

스프링 부트는 datasource 설정이 없으면, 기본적으로 메모리 DB를 사용하고, driver-class도 현재 등록된 라이브러리를 보고 찾아준다. 추가로 ddl-auto도 create-drop 모드로 동작한다. 따라서 데이터소스나, JPA 관련된 별도의 추가 설정을 하지 않아도 된다.

 

-> h2를 꺼도 Test Case가 동작을 하는 것을 볼 수 있음

- DB 띄우지 않고 편하게 테스트 가능하며, 사실 yml에 다른 설정을 적어주지 않으면 기본적으로 메모리 모드로 돌림

- 위 내용 다 없어도 되며, 운영과 테스트에서의 yml은 서로 다르게 설정하는 것이 맞음

 

기본적으로 spring은 crate-drop 방식을 사용한다

1) create: table 전부 drop 시킨 후에 create

2) create-drop: create와 동일하지만 마지막에 애플리케이션 종료 시점에 drop query를 다 날려 주어 완전히 깨끗하게 초기화 해줌(자원 정리)

728x90
728x90