[우아한테크코스] 5월 7일 TIL

2 minute read

[Spring] test 만들기

@SpringBootTest로 만든다.
@Transactional을 주면 테스트를 하고 db에 넣었던 것을 rollback해서 테스트를 반복할 수 있다.
db에 넣은 것을 rollback 안하고 싶으면 @Commit 어노테이션 사용
전체 테스트에서 걸리는 경우 @DirtiesContext를 참고
이것보다 ddl을 새로 만드는 것을 선호

[Spring] jpa의 사용

jpa는 반복 코드를 줄일 수 있고, 데이터 중심이 아닌 객체 중심 설계를 해 개발 생산성을 높일 수 있다.
라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.h2database:h2' testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

jpa 설정 추가

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

@Entity로 테이블 생성
@Id, @GeneratedValue(strategy = GenerationType.IDENTITY)로 매핑

Repository에 EntityManager 주입
persist(객체): save
find(객체.class, id): findById
특정 객체로 조회는 아래와 같이 할 수 있다.

public List<Member> findAll() {
          return em.createQuery("select m from Member m", Member.class)
                  .getResultList();
}
      public Optional<Member> findByName(String name) {
          List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
                  .setParameter("name", name)
                  .getResultList();
          return result.stream().findAny();
}

jpa는 항상 @Transactional이 필요하다. Service 객체 상단에 부여

스프링 데이터 jpa

public interface SpringDataJpaMemberRepository extends JpaRepository<Member,
    Long>, MemberRepository {
        Optional<Member> findByName(String name);
    }

jpa가 SpringDataJpaMemberRepository를 스프링 빈으로 자동 등록
그러면 대부분의 기본적인 메서드를 제공해준다.
인터페이스의 이름만으로도 알아서 메서드를 매핑해준다.
동적 쿼리는 Querydsl이라는 라이브러리를 사용한다. 혹은 JdbcTemplate

[Spring] AOP

모든 메서드에 유사한 로직이 추가되어야 한다면?
핵심 관심 사항과 공통 관심 사항을 분리하자
중간에 intercept나, 계속 실행하거나 원하는대로 할 수 있다.
아래 코드와 같이 적용
aop는 가짜(프록시) 메서드를 띄워놓고 돌면서 실제 메서드 실행
di를 하니까 이렇게 가짜로 두는게 가능하다.

  @Component
  @Aspect
  public class TimeTraceAop {
      @Around("execution(* hello.hellospring..*(..))")  //적용할 곳, 여기를 조정해서 원하는 곳에만 사용하도록 할 수도 있다.
      public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
          long start = System.currentTimeMillis();
          System.out.println("START: " + joinPoint.toString());
          try {
              return joinPoint.proceed();   //다음 함수 실행  
          } finally {
              long finish = System.currentTimeMillis();
              long timeMs = finish - start;
              System.out.println("END: " + joinPoint.toString()+ " " + timeMs + "ms");
            }
      }
  }        

[Spring] 좋은 테스트 짜는 법

좋은 테스트는 프로덕션 코드보다 2~3배 길다.
테스트는 상호 독립적으로 작성해야 한다.
이를 위해서는 공유되는 자원을 잘 관리해야 한다.
메모리 db에서 rollback 되는 경우는 bean이 새로 생성되어서이다.
DirtiesContext는 기존의 컨텍스트를 더럽혀서 새로운 빈(컨텍스트)을 사용하도록 한다는 것이다.
@Transactional을 사용하면 프록시 먼저 만들어지고 실행하면서 코드 굴러가게 하는데 어노테이션 하나 붙은걸 실행하면 commit을 해라. 실패하면 rollback 하라는 것
테스트에서는 commit이 안되고 rollback이 default 값이 true로 되어 rollback하도록 한다.

  • 테스트의 독립성 보장 방법
    deleteAll 메서드 만들기
    db 초기화
    스프링 컨테이너 초기화(DirtiesContext를 사용: 모든 bean들을 reload 하게 함으로써 테스트 비용이 많이 듦)

mockBean 사용 시 dirtiesContext와 같이 동작해서 성능에 문제가 있을 수 있다.
느리고 비쌈 <—-E2E—-Integration—-Unit—-> 빠르고 쌈
테스트의 목적에 따라 사용하는 것이 중요하다.
테스트의 의도를 명확히 드러내고 유지보수의 대상으로 보기 위해 가독성 좋게 짜는 연습도 필요하다.
ExtractableResponse사용해보기
어떻게 테스트를 구현하지? 보다는 어떤 목적으로 구현하지?를 주로 생각하기

E2E test
integration test: 독립된 단위가 서로 연결될 때 올바르게 작동하는지 확인하는 테스트
unit test

atdd vs tdd
test