Spring Boot 애플리케이션을 테스트하기 위한 기능을 제공하는 기술
애플리케이션을 개발하다 보면 다음과 같은 것을 확인해야 합니다.상품이 정상적으로 저장되는가?
상품을 정상적으로 조회할 수 있는가?
주문이 정상적으로 생성되는가?
주문상품이 주문과 연결되는가?
Controller의 API가 정상적으로 응답하는가?
Repository가 DB와 정상적으로 동작하는가?
Plain Text
복사
이러한 기능을 자동으로 검증하는 코드를 작성하는 것이 테스트입니다.
테스트를 사용하는 이유
테스트를 하지 않으면 기능을 수정할 때 기존 기능이 정상적으로 동작하는지 직접 확인해야 합니다.
코드 수정
↓
서버 실행
↓
브라우저 / Postman 실행
↓
직접 API 호출
↓
결과 확인
Plain Text
복사
테스트를 작성하면:
코드 수정
↓
테스트 실행
↓
성공 / 실패 확인
Plain Text
복사
따라서 테스트를 작성해두면 코드 변경에 따른 오류를 빠르게 발견할 수 있습니다.
Spring Boot Test 의존성
Spring Boot에서는 일반적으로 다음 의존성을 사용합니다.
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Plain Text
복사
spring-boot-starter-test에는 테스트에 필요한 여러 라이브러리가 포함되어 있습니다.
spring-boot-starter-test
│
├── JUnit 5
├── Spring Test
├── Spring Boot Test
├── Mockito
├── AssertJ
└── Hamcrest
Plain Text
복사
JUnit 5
JUnit은 Java 테스트 코드를 작성하고 실행하기 위한 테스트 프레임워크입니다.
기본적인 테스트는 다음과 같이 작성합니다.
@Testvoidtest() {
System.out.println("테스트 실행");
}
Java
복사
@Test가 붙은 메서드는 테스트 메서드로 인식됩니다.
테스트의 기본 구조
테스트는 보통 다음과 같은 구조로 작성합니다.
Given
↓
테스트에 필요한 데이터 준비
When
↓
테스트할 기능 실행
Then
↓
결과 검증
Plain Text
복사
예를 들어 상품 조회라면:
@Test
void 상품_조회() {
// Given
Integer no = 1;
// When
Product product = productService.select(no);
// Then
assertThat(product).isNotNull();
}
Java
복사
Assertions (검증)
“기대한 결과가 실제 결과와 맞는지 검증하는 기능”이에요.
주요 검증 메소드
메소드 | 의미 | 예시 |
isNotNull() | 값이 null이 아님 | assertThat(result).isNotNull(); |
isNull() | 값이 null임 | assertThat(result).isNull(); |
isEqualTo(expected) | 실제 값이 기대 값과 같음 | assertThat(result).isEqualTo("노트북"); |
isNotEqualTo(unexpected) | 실제 값이 특정 값과 다름 | assertThat(result).isNotEqualTo("마우스"); |
isTrue() | 값이 true임(주로 boolean) | assertThat(result).isTrue(); |
isFalse() | 값이 false임(주로 boolean) | assertThat(result).isFalse(); |
Spring Boot Test에서는 AssertJ를 많이 사용합니다.
assertThat(product).isNotNull();
Java
복사
대표적인 검증 방법:
assertThat(result).isNotNull();
assertThat(result).isNull();
assertThat(result).isEqualTo("노트북");
assertThat(result).isNotEqualTo("마우스");
assertThat(result).isTrue();
assertThat(result).isFalse();
Java
복사
@SpringBootTest
Spring Boot에서 가장 대표적으로 사용하는 테스트 어노테이션입니다.
@SpringBootTest
class ProductTest {
@Test
void test() {
}
}
Java
복사
@SpringBootTest는 Spring Boot 애플리케이션의 테스트 환경을 구성합니다.
즉,
@SpringBootTest
↓
Spring Container 생성
↓
Bean 등록
↓
Service / Repository 등 사용 가능
↓
테스트 실행
Plain Text
복사
@Autowired와 테스트
Spring Bean을 테스트에서 주입받을 수도 있습니다.
@SpringBootTest
class ProductServiceTest {
@Autowired
ProductService productService;
@Test
void 상품_조회() {
Product product =
productService.select(1);
assertThat(product).isNotNull();
}
}
Java
복사
실제 Spring Container에 등록된 ProductService를 주입받아서 테스트합니다.
쇼핑몰 상품, 주문 테스트 실습
1) 상품 테스트
flowchart TD
D["상품 5개 저장"]
D --> E["전체 상품 조회<br/>findAll()"]
E --> F{"전체 개수 = 5개?"}
F -->|예| G["'데님 팬츠' 조회"]
G --> H["이름 / 가격 검증"]
H --> I["테스트 종료"]
I --> J["트랜잭션 롤백"]Mermaid
복사
•
환경 준비: @DataJpaTest로 JPA 관련 구성만 로딩, Repository 주입
•
상품 저장 테스트: 상품 5개 저장
•
상품 조회 테스트: 전체 조회(findAll())
•
검증
◦
전체 개수 5개인지 확인
◦
특정 상품(예: “데님 팬츠”)을 찾아 이름/가격 검증
•
마무리: 기본 트랜잭션으로 롤백되어 DB에 데이터가 남지 않음
2) 주문 통합 테스트
flowchart TD
D["상품 5개 등록"]
D --> E["저장 결과 / 첫 상품 정보 검증"]
E --> F["주문 생성"]
F --> G["주문명 생성 규칙 검증"]
G --> H["주문항목 3개 등록<br/>수량 1, 2, 3"]
H --> I["주문 조회"]
I --> J["주문항목 3개 검증"]
J --> K["총액 검증<br/>단가 × 수량 합"]
K --> L["주문항목 단건 조회"]
L --> M["주문 ↔ 상품<br/>연관관계 검증"]
M --> N["주문별 주문항목 목록 조회"]
N --> O["목록 개수 = 3개"]
O --> P["Repository count()"]
P --> Q["저장 건수 최종 검증"]
Q --> R["테스트 종료"]
R --> S["@Transactional 롤백"]Mermaid
복사
•
환경 준비: @SpringBootTest + @Transactional로 스프링 컨테이너 로딩, 서비스 Bean 주입
•
상품 등록 테스트: 상품 5개 등록 → 저장/첫 상품 정보가 정상인지 검증
•
주문 생성 테스트: 주문 생성(주문명 생성 규칙 포함)
•
주문항목 등록 테스트: 주문항목 3개 등록(수량 1,2,3)
•
주문 조회 테스트: 주문 조회 → 주문항목 3개 / 총액(단가×수량 합) 검증
•
연관관계 테스트: 주문항목 단건 조회 → 주문-상품 연결 확인
•
목록 조회 테스트: 주문별 주문항목 목록 조회 → 목록 개수(3개) 검증
•
리포지토리 최종 검증: count()로 저장 건수 확인
•
마무리: @Transactional로 롤백되어 DB에 데이터가 남지 않음
ProductRepositoryTest.java
package com.aloha.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import com.aloha.jpa.dao.ProductRepository;
import com.aloha.jpa.domain.Product;
@DataJpaTest
class ProductRepositoryTest {
@Autowired
ProductRepository productRepository;
// 픽스처: 서로 다른 이름/가격의 상품 5개
private static final String[] PRODUCT_NAMES = {
"오버핏 후드티", "데님 팬츠", "니트 가디건", "와이드 슬랙스", "트렌치 코트"
};
private static final int[] PRODUCT_PRICES = {
59000, 79000, 99000, 69000, 129000
};
@Test
void 상품_5개_저장_조회() {
for (int i = 0; i < PRODUCT_NAMES.length; i++) {
Product product = new Product();
product.setName(PRODUCT_NAMES[i]);
product.setPrice(PRODUCT_PRICES[i]);
productRepository.save(product);
}
List<Product> products = productRepository.findAll();
// 등록한 상품 5개가 모두 저장되었는지 확인
assertThat(products).hasSize(5);
// 저장 순서가 보장되지 않으므로 이름으로 대상 상품을 찾음
Product selected = products.stream()
.filter(p -> p.getName().equals("데님 팬츠"))
.findFirst()
.orElse(null);
// 이름과 가격이 등록한 값 그대로 저장되었는지 확인
assertThat(selected).isNotNull();
assertThat(selected.getName()).isEqualTo("데님 팬츠");
assertThat(selected.getPrice()).isEqualTo(79000);
}
}
Java
복사
OrderIntegrationTest.java
package com.aloha.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import com.aloha.jpa.dao.OrderItemRepository;
import com.aloha.jpa.domain.Orders;
import com.aloha.jpa.domain.OrderItem;
import com.aloha.jpa.domain.Product;
import com.aloha.jpa.dto.OrderItemRequest;
import com.aloha.jpa.service.OrderItemService;
import com.aloha.jpa.service.OrderService;
import com.aloha.jpa.service.ProductService;
@SpringBootTest
@Transactional
class OrderIntegrationTest {
@Autowired
ProductService productService;
@Autowired
OrderService orderService;
@Autowired
OrderItemService orderItemService;
@Autowired
OrderItemRepository orderItemRepository;
// 픽스처: 상품 5개 중 앞 3개로 주문항목(수량 1, 2, 3) 구성
private static final String[] PRODUCT_NAMES = {
"오버핏 후드티", "데님 팬츠", "니트 가디건", "와이드 슬랙스", "트렌치 코트"
};
private static final int[] PRODUCT_PRICES = {
59000, 79000, 99000, 69000, 129000
};
private static final int[] ORDER_ITEM_QUANTITIES = { 1, 2, 3 };
@Test
void 패션쇼핑몰_주문_전체과정() {
// 1. 상품 5개 등록
List<Product> savedProducts = new ArrayList<>();
for (int i = 0; i < PRODUCT_NAMES.length; i++) {
Product product = new Product();
product.setName(PRODUCT_NAMES[i]);
product.setPrice(PRODUCT_PRICES[i]);
savedProducts.add(productService.insert(product));
}
// 5개 모두 저장되고 DB가 채번한 PK(no)가 부여되었는지 확인
assertThat(savedProducts).hasSize(5);
assertThat(savedProducts.get(0).getNo()).isNotNull();
assertThat(savedProducts.get(0).getName()).isEqualTo("오버핏 후드티");
// 2. 주문 생성 ("첫 상품명 외 N개" 형식의 주문명)
int orderItemCount = ORDER_ITEM_QUANTITIES.length;
Orders order = new Orders();
order.setOrderName(
savedProducts.get(0).getName() + " 외 " + (orderItemCount - 1) + "개"
);
Orders savedOrder = orderService.insert(order);
// 주문이 저장되고 주문명이 의도한 포맷대로 조합되었는지 확인
assertThat(savedOrder.getNo()).isNotNull();
assertThat(savedOrder.getOrderName()).isEqualTo("오버핏 후드티 외 2개");
// 3. 주문항목 3개 등록 (수량 1, 2, 3)
List<OrderItem> savedItems = new ArrayList<>();
for (int i = 0; i < orderItemCount; i++) {
OrderItemRequest request = new OrderItemRequest();
request.setOrderNo(savedOrder.getNo());
request.setProductNo(savedProducts.get(i).getNo());
request.setQuantity(ORDER_ITEM_QUANTITIES[i]);
savedItems.add(orderItemService.insert(request));
}
// 주문항목 3개가 각각 의도한 수량(1, 2, 3)으로 저장되었는지 확인
assertThat(savedItems).hasSize(orderItemCount);
for (int i = 0; i < orderItemCount; i++) {
assertThat(savedItems.get(i).getQuantity())
.isEqualTo(ORDER_ITEM_QUANTITIES[i]);
}
// 4. 주문 조회 (총액 검증) - 서비스가 누적한 totalAmount와 비교할 기대값 계산
int expectedTotalAmount = 0;
for (int i = 0; i < orderItemCount; i++) {
expectedTotalAmount += PRODUCT_PRICES[i] * ORDER_ITEM_QUANTITIES[i];
}
Orders selectedOrder =
orderService.select(savedOrder.getNo());
assertThat(selectedOrder).isNotNull();
assertThat(selectedOrder.getOrderItems()).hasSize(orderItemCount);
assertThat(selectedOrder.getTotalAmount())
.isEqualTo(expectedTotalAmount);
// 5. 주문상품 조회
OrderItem selectedItem =
orderItemService.select(savedItems.get(0).getNo());
// 주문항목에 연관된 상품/주문이 올바르게 매핑되어 있는지 확인
assertThat(selectedItem).isNotNull();
assertThat(selectedItem.getProduct().getName())
.isEqualTo("오버핏 후드티");
assertThat(selectedItem.getOrder().getNo())
.isEqualTo(savedOrder.getNo());
// 6. 주문별 주문상품 조회
assertThat(
orderItemService.listByOrderNo(savedOrder.getNo())
).hasSize(orderItemCount);
// 실제 Repository에도 저장되었는지 최종 확인
assertThat(orderItemRepository.count()).isEqualTo(orderItemCount);
}
}
Java
복사





