본문 바로가기

Spring/단위테스트

2) Spring boot 단위테스트 2부 - JPA @Repository 테스트

반응형

1부에 앞서 2부는 JPA Repositry에 대한 테스트를 해보고자 한다.

먼저 테스트를 위한 Repository와 Entity 들이다.

package com.devracoon.jpa.entity;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import org.hibernate.annotations.GenericGenerator;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Data
@Entity
@RequiredArgsConstructor
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "jsonId")
public class Product {

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
    @Column(name = "product_id", columnDefinition = "VARCHAR(255)")
    private String productId;
    
    @NonNull
    @Column(name = "product_name", columnDefinition = "VARCHAR(255)")
    private String productName;
    
    @OneToMany(mappedBy = "product" , targetEntity = Item.class , fetch = FetchType.LAZY , cascade = {CascadeType.ALL})
    private List<Item> items = new ArrayList<Item>();
    
}
package com.devracoon.jpa.entity;

import javax.persistence.Column;
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 org.hibernate.annotations.GenericGenerator;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Data
@Entity
@RequiredArgsConstructor
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "jsonId")
public class Item {

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
    @Column(name = "item_id", columnDefinition = "VARCHAR(255)")
    private String itemId;
    
    @ManyToOne(targetEntity = Product.class ,fetch = FetchType.LAZY )
    @JoinColumn(name = "product_id")
    private Product product;
    
    @NonNull
    @Column(name = "item_name", columnDefinition = "VARCHAR(255)")
    private String itemName;
    
    @Column(name = "item_number", columnDefinition = "VARCHAR(255)")
    private String itemNumber;
}
package com.devracoon.jpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.devracoon.jpa.entity.Product;

@Repository
public interface ProductRepository extends JpaRepository<Product, String> {

}

 

간단한 Parent-child 구조의 Entity와 기본 Repository를 만들었다. 

 

이제 Test 소스를 구현해 보자.

 

package com.devracoon.jpa.repository;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import com.devracoon.jpa.entity.Item;
import com.devracoon.jpa.entity.Product;

@DataJpaTest
class ProductRepositoryTest {

    @Autowired
    private ProductRepository productRepo; 

    @Test
    void testSave() {
        Product product = new Product();
        product.setProductName("tset-prd");
        
        List<Item> items = new ArrayList<Item>();
        for(int i = 0 ; i < 4 ; i++) {
            Item item = new Item();
            item.setItemId("test-item-"+i);
            item.setItemName("test-item-"+i);
            item.setItemNumber("test-item-"+i);
            item.setProduct(product);
        }
        product.setItems(items);
        productRepo.save(product);
        
        String productId = product.getProductId();
        Product findProduct = productRepo.findById(productId).orElse(null);
        
        Assertions.assertNotNull(findProduct);
        Assertions.assertEquals(findProduct.getProductId(), productId);
        
        
    }

}

@DataJpaTest 는 Repository 관련 설정만 읽어와서 spring context를 만들고 @Test 함수가 실행가능하게 만들어 준다.

 

@DataJpaTest는 기본적으로 h2 memory DB를 생성해서 테이블을 만들고 데이터를 임시 생성한 DB에 저장하고 읽는 테스트를 진행하게 되는데 해당 java 파일을 실행하면 DB가 새롭게 생성되고 테이블이 만들어지는것이 확인된다.

 

2021-03-25 09:26:03.317  INFO 1780 --- [           main] c.d.j.repository.ProductRepositoryTest   : Starting ProductRepositoryTest using Java 1.8.0_144 on DESKTOP-BQBKFID with PID 1780 (started by kangzu8743 in C:\kangzu8743_test_proejct\sts_workspace\SpringJpaTest)
2021-03-25 09:26:03.320  INFO 1780 --- [           main] c.d.j.repository.ProductRepositoryTest   : The following profiles are active: local
2021-03-25 09:26:04.092  INFO 1780 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-03-25 09:26:04.293  INFO 1780 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 186 ms. Found 3 JPA repository interfaces.
2021-03-25 09:26:04.421  INFO 1780 --- [           main] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version
2021-03-25 09:26:04.863  INFO 1780 --- [           main] o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:c7baebb8-c8fe-4b12-ae56-bc7688473550;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
2021-03-25 09:26:05.866 DEBUG 1780 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : PersistenceUnitInfo [
	name: default
	persistence provider classname: null
	classloader: sun.misc.Launcher$AppClassLoader@79fc0f2f
	excludeUnlistedClasses: true
	JTA datasource: null
	Non JTA datasource: org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy@3fa76c61
	Transaction type: RESOURCE_LOCAL
	PU root URL: file:/C:/kangzu8743_test_proejct/sts_workspace/SpringJpaTest/bin/main/
	Shared Cache Mode: UNSPECIFIED
	Validation Mode: AUTO
	Jar files URLs []
	Managed classes names [
		com.devracoon.jpa.entity.Item
		com.devracoon.jpa.entity.Product
		com.devracoon.jpa.entity.ProductOrder
		com.devracoon.jpa.entity.ProductOrderItem
		com.devracoon.jpa.entity.User]
	Mapping files names []
	Properties []

위 로그를 보면 새롭게 h2 memory DB가 만들어지는것이 보인다. 

이상태로 테스트를 진행해도 되겠지만 보통은 로컬에서 사용중이던 DB나 개발서버 DB등을 이용해서 테스트하고 

기본 쌓여있던 데이터를 조회하는 테스트를 진행할텐데 새롭게 생성된 DB를 테스트하는것은 정확한 테스트가 안될것 같다. 

 

그럼 기존 로컬에서 개발할때 쓰던 DB 연결을 어떻게 해야 할까?

소스를 아래와 같이 변경하면된다.

package com.devracoon.jpa.repository;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.Import;

import com.devracoon.jpa.H2ServerConfiguration;
import com.devracoon.jpa.entity.Item;
import com.devracoon.jpa.entity.Product;

@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
@Import(H2ServerConfiguration.class)
class ProductRepositoryTest {

    @Autowired
    private ProductRepository productRepo; 

    @Test
    void testSave() {
        Product product = new Product();
        product.setProductName("tset-prd");
        
        List<Item> items = new ArrayList<Item>();
        for(int i = 0 ; i < 4 ; i++) {
            Item item = new Item();
            item.setItemId("test-item-"+i);
            item.setItemName("test-item-"+i);
            item.setItemNumber("test-item-"+i);
            item.setProduct(product);
        }
        product.setItems(items);
        productRepo.save(product);
        
        String productId = product.getProductId();
        Product findProduct = productRepo.findById(productId).orElse(null);
        
        Assertions.assertNotNull(findProduct);
        Assertions.assertEquals(findProduct.getProductId(), productId);
        
        
    }

}

 

@AutoConfigureTestDatabase(replace = Replace.NONE)
@Import(H2ServerConfiguration.class)

위 두개의 어노테이션을 추가했다. 자동을 설정되는 DB 정보를 사용하지 않겠다는뜻이고

기존 H2ServerConfiuration을 쓰겠다는 의미이다. 

 

이렇게 하면 로컬에서 profile local 일때 설정된 DB 정보를 읽어오게 된다. 

실제로 DB를 새롭게 만들지 않기 때문에 테스트 실행 속도도 휠씬 빠르고

데이터 Save 테스트도 테스트 실행이 끝나면 Rollback이 되기 때문에 문제가 되지 않는다.

 

이렇게 간단히 Reposiry 테스트를 해봤다.