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/
reflectoring.io/spring-boot-data-jpa-test/
www.inflearn.com/questions/17219
hellokoding.com/spring-boot-test-data-layer-example-with-datajpatest/
'Spring 이해하기' 카테고리의 다른 글
스프링이란 무엇인가? (0) | 2021.01.06 |
---|---|
[JPA] Field or Property access? (0) | 2020.12.13 |
@Fetch(FetchMode.SUBSELECT) 과 IN subquery 는 왜 느릴까? (0) | 2020.10.14 |
LazyInitializationException 를 벗어나기 위해서는 어떻게 해야될까? JPA에서 최적의 쿼리를 보내는 방법은 무엇일까? (0) | 2020.10.03 |
대체 JPA에서 Proxy 로 초기화된다는 말이 뭔데? (0) | 2020.10.02 |
댓글