리포지토리는 개발자가 구현하는 레이어 중 가장 DB와 가까움
또한 JpaRepository를 상속받아 기본적인 쿼리 메서드 사용 가능
-> 구현하는 목적에 대해 특별히 고민하고 작성하자
우선 findById(), save()와 같은 기본 메서드에 대한 테스트는 별 의미 없음
-> 이미 검증을 마치고 제공되는 것
DB 연동 여부
는 테스트 시 고려해볼 사항
DB는 굳이 따지면 외우 요인인데 만약 단위 테스트를 고려한다면 제외하기도 함
-> 테스트 용으로 다른 DB를 사용하기도 함
-> DB를 사용하는 테스트는 테스트 과정에서 DB에 테스트 데이터가 적재됨
그래서 DB를 연동한 테스트는 테스트 데이터를 제거하는 코드까지 포함해서 작성하는 것이 좋음!!
하지만 코드가 실행되면서 발생하는 사이드 이펙트 까지 고려시 연동없이 테스트 하는 것이 좋을수도 있음
우선 DB를 제외한 테스트 상황을 가정해 테스트 데이터베이스로 H2 DB
사용
보통 테스트 환경에서는 별도의 설정이 없다면 임베디드 DB 사용
의존성 추가
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
데이터 베이스 저장 테스트
@DataJpaTest
public class ProductRepositoryTestByH2 {
@Autowired
private ProductRepository productRepository;
@Test
void saveTest(){
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1000);
//when
Product savedProduct = productRepository.save(product);
//then
assertEquals(product.getName(), savedProduct.getName());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
데이터베이스 조회 테스트
@Test
void selectTest(){
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1000);
//객체를 DB에 바로 저장
Product savedProduct = productRepository.saveAndFlush(product);
//when
Product foundProduct = productRepository.findById(savedProduct.getNumber()).get();
//then
assertEquals(product.getName(), foundProduct.getName());
assertEquals(product.getPrice(), foundProduct.getPrice());
assertEquals(product.getStock(), foundProduct.getStock());
}
JPA와 관련된 설정만 로드해서 테스트 진행
기본적으로 @Transactional
어노테이션을 포함하고 있어 테스트 코드가 종료되면 자동으로 DB의 롤백이 진행!!
기본값으로 임베디드 DB사용 -> 다른 DB를 사용할려면 설정 바꾸자
이 어노테이션을 선언하여 리포지토리를 정상적으로 주입받을수 있음
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Test
void save(){
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1000);
Product savedProduct = productRepository.save(product);
assertEquals(product.getName(), savedProduct.getName());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
replace의 요소는 @AutoConfigureTestDatabase
어노테이션의 값을 조정하는 작업 수행
replace 속성의 기본값은 Replace.ANY
이며 이경우 임베디드 메모리 DB를 사용
None으로 변경 시 실제로 사용하는 DB로 테스트 가능
@SpringBootTest
public class ProductRepositoryTest2 {
@Autowired
ProductRepository productRepository;
@Test
public void basicCRUDTest(){
/* create */
// given
Product givenProduct = Product.builder()
.name("노트")
.price(1000)
.stock(500).build();
//when
Product savedProduct = productRepository.save(givenProduct);
//then
Assertions.assertThat(savedProduct.getNumber())
.isEqualTo(givenProduct.getNumber());
Assertions.assertThat(savedProduct.getName())
.isEqualTo(givenProduct.getName());
Assertions.assertThat(savedProduct.getPrice())
.isEqualTo(givenProduct.getPrice());
Assertions.assertThat(savedProduct.getStock())
.isEqualTo(givenProduct.getStock());
/* read */
//when
Product selectedProduct = productRepository.findById(savedProduct.getNumber())
.orElseThrow(RuntimeException::new);
//then
Assertions.assertThat(savedProduct.getNumber())
.isEqualTo(givenProduct.getNumber());
Assertions.assertThat(savedProduct.getName())
.isEqualTo(givenProduct.getName());
Assertions.assertThat(savedProduct.getPrice())
.isEqualTo(givenProduct.getPrice());
Assertions.assertThat(savedProduct.getStock())
.isEqualTo(givenProduct.getStock());
/* update */
//when
Product foundProduct = productRepository.findById(selectedProduct.getNumber())
.orElseThrow(RuntimeException::new);
foundProduct.setName("장난감");
Product updatedProduct = productRepository.save(foundProduct);
//then
assertEquals(updatedProduct.getName(), "장난감");
/*delete*/
//when
productRepository.delete(updatedProduct);
//then
assertFalse(productRepository.findById(selectedProduct.getNumber()).isPresent());
}
}
CRUD 모든 기능을 한 테스트 코드에 작성
@SpringBootTest 어노테이션을 활용하면 스프링의 모든 설정을 가져오고 빈 객체도 전체를 스캔하기에 의존성 주입 고민할 필요 X
다만 테스트 속도가 느림