본문 바로가기
Spring 이해하기

@DataJpaTest 흟어보기

by simplify-len 2020. 12. 12.

Spring JPA에서 Repository 테스트를 하기 위한 애노테이션으로 @DataJpaTest 를 사용합니다.

왜 Repository 를 테스트 해야 하는걸까?

반버논의 도메인 주도설계에서 테스트를 하는 이유는 아래와 같은 이유라고 합니다.

"#리파지토리의 테스트를 바라보는 두 가지 방향
1. 리파지토리 자체가 바르게 동작하는지 증명하기 위해서
2. 그들이 생성하는 애그리게잇을 저장하고 기존의 애그리게잇을 검색하기 위해 리파지토리를 사용하는 코드를 테스트한다.

첫번째 유형의 테스트에선 완전한 프로덕션 수준 품질의 구현을 사용해야 한다. 그렇지 않으면 프로덕션 코드가 작동할지 알 수 있는 방법이 없다. 두번 째 유형의 테스트에선 프로덕션 구현을 사용하거나 그 대신 인메모리 구현을 사용할 수 있다."

즉, Repository의 기능이 잘 동작되는지 테스트하기 위해서이다.

간단히 DataJpaTest를 활용하는 예제 코드는 아래와 같습니다.

예제 코드

package com.jpa.test.demo.domain;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Customer {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;

  @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
  private Set<Order> orders = new HashSet<>();

  public Customer(String name) {
    this.name = name;
  }

  public void addOrder(Order order) {
    orders.add(order);
  }

  @Override
  public String toString() {
    return "Customer{" +
        "id=" + id +
        ", name='" + name + '\'' +
        ", orders=" + orders +
        '}';
  }
}
package com.jpa.test.demo.domain;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "orders")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Order {

  @Id
  @GeneratedValue
  private Long id;

  private String name;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "customer_id")
  private Customer customer;

  public Order(String name) {
    this.name = name;
  }

  public Order(String name, Customer customer) {
    this.name = name;
    this.customer = customer;
  }

  public void setCustomer(Customer customer){
    this.customer = customer;
    customer.addOrder(this);
  }
}
package com.jpa.test.demo.repository;

import com.jpa.test.demo.domain.Customer;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CustomerRepository extends JpaRepository<Customer, Long> {

  Optional<Customer> findCustomerByName(String name);

  Optional<Customer> findByName(String name);

  Optional<Customer> findAllByName(String name);
}
package com.jpa.test.demo.repository;

import com.jpa.test.demo.domain.Order;
import org.springframework.data.repository.CrudRepository;

public interface OrdersRepository extends CrudRepository<Order, Long> {

}

 

테스트 코드는 아래와 같습니다.

package com.jpa.test.demo.repository;

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

import com.jpa.test.demo.domain.Customer;
import com.jpa.test.demo.domain.Order;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit.jupiter.SpringExtension;

//@ExtendWith(SpringExtension.class)
//@ExtendWith(MockitoExtension.class)
@DataJpaTest
class CustomerRepositoryTest {

  @Autowired
  TestEntityManager testEntityManager;

//  @Autowired private DataSource dataSource;
//  @Autowired private JdbcTemplate jdbcTemplate;
//  @Autowired private EntityManager entityManager;

//
//  @Test
//  void name() {
//    assertNotNull(dataSource);
//    assertNotNull(jdbcTemplate);
//    assertNotNull(entityManager);
//  }

  @Autowired
  private CustomerRepository customerRepository;

  @BeforeEach
  void setUp() {
    Customer customer = new Customer("김정규");
    Order order1 = new Order("order1");
    Order order2 = new Order("order2");
//    testEntityManager.persist(customer);
    testEntityManager.persist(order1);
    testEntityManager.persist(order2);

  }

  @Test
  void customerRepositoryIsNotNull() {
    assertNotNull(customerRepository);
  }

  @Test
  void should_customer_has_name() {
//    assertEquals("1", "1");
    Optional<Customer> kim1 = customerRepository.findByName("김정규");
    Optional<Customer> kim3 = customerRepository.findCustomerByName("김정규");

    Customer actual = kim1.get();
    Customer actual3 = kim3.get();
    System.out.println(actual.toString());
    assertEquals(actual.getName(), "김정규");
    assertEquals(actual3.getName(), "김정규");
  }

  @Test
  void should_order_save_is_right() {
    Customer customerA = new Customer("김정규");
    Customer customerB = new Customer("박연수");
    Order order = new Order("order1");
    Order order2 = new Order("order2");

    order.setCustomer(customerA);
    order2.setCustomer(customerA);

    Customer save = customerRepository.save(customerA);

    Order order3 = new Order("order3", customerB);
    Order order4 = new Order("order4", customerB);

    customerB.addOrder(order3);
    customerB.addOrder(order4);

    Customer save2 = customerRepository.save(customerB);

    assertEquals(save, customerA);

    assertEquals(save2.getOrders().size(), 2);

    Optional<Customer> customer = customerRepository.findCustomerByName("박연수");

    assertEquals(customer.get().getOrders().size(), 2);

  }
}

mappedBy - 연관 관계 맵핑에서 주인을 선정하는 Key

order3, order4 객체를 생성한다고 해서 저장되지 않는다.


 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DataJpaTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ImportAutoConfiguration
public @interface DataJpaTest {
		...
}

@DataJpaTest안에 @ExtendWith(SpringExtension.class) 가 있기 때문에 같이 추가할 필요가 없다.


TestEntityManager 라는 것이 있는데, 이는 테스트를 위해서 나온 것으로 유용한 메소드를 제공합니다. persistFlushFind 와 같은 메소드

 

[참고자료]

reflectoring.io/unit-testing-spring-boot/

 

Unit Testing with Spring Boot

In this tutorial we'll learn how to build testable Spring beans and get to know the tools that Spring Boot by default imports for writing maintainable unit tests.

reflectoring.io

reflectoring.io/spring-boot-data-jpa-test/

 

Testing JPA Queries with Spring Boot and @DataJpaTest

An in-depth tutorial about Spring Boot's support for testing JPA database queries.

reflectoring.io

www.inflearn.com/questions/17219

 

기존 테이블이 삭제되지 않는 문제.. - 인프런

질문 - 기존 테이블이 삭제되지 않는 문제.. 안녕하세요 영한님 강좌를 보며 예제를 따라하던중 제 프로젝트가 영한님과 다르게 실행되는것 같아 질문을 올립니다 hibernate.hbm2ddl.auto = create 인 상

www.inflearn.com

hellokoding.com/spring-boot-test-data-layer-example-with-datajpatest/

 

Spring Boot @DataJPATest Example of Testing Data Layer

In this tutorial, you will learn to implement an integration test of the JPA and Hibernate data layer in Spring Boot by using @DataJPATest annotation with in-memory database @DataJPATest provides the following features Configure the in-memory test database

hellokoding.com

brunch.co.kr/@springboot/207

 

스프링부트 테스트(1)

스프링 부트 Unit Test 및 Integration Test | 테스트코드를 작성하는 일은 정말 중요하다. 하지만, 필자에게 아직도 너무 어려운 일이 바로 테스트 코드를 작성하는 일이다. TDD 를 잘하는 개발자는 필

brunch.co.kr

 

댓글