프로젝트 환경
| 종류 | 환경 |
|---|
| IDE | Intellij IDEA 2023.1.2 (Ultimate Edition) |
| 언어 | SpringBoot 3.1.0 |
| 타입 | Gradle - Groovy |
| JDK | corretto-17 |
| 패키지 생성 | Jar |
| 버전관리 | Github |
의존성
| 종류 | 이름 |
|---|
| Web | Spring web |
| Developer Tools | Spring Processor / Lombok |
| SQL | Spring Data JPA / MySQL Driver |
| 기타 | OpenAPI:2.0.2 / Gson:2.10.1 / Spring test |
Junit 의존성 추가
코드
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-test' //Junit 테스트
implementation 'com.google.code.gson:gson:2.10.1' //gson
}
test/TestLifeCycle.java
package com.springboot.hello;
import org.junit.jupiter.api.*;
public class TestLifeCycle {
@BeforeAll
static void beforeAll() {
System.out.println("## beforeAll Annotation 호출 ##\n");
}
@AfterAll
static void afterAll() {
System.out.println("## afterAll Annotation 호출 ##\n");
}
@BeforeEach
void beforeEach() {
System.out.println("## beforeEach Annotation 호출 ##\n");
}
@AfterEach
void afterEach() {
System.out.println("## afterEach Annotation 호출 ##\n");
}
@Test
void test1() {
System.out.println("## test1 시작 ##\n");
}
@Test
@DisplayName("Test Case 2!!!")
void test2() {
System.out.println("## test2 시작 ##\n");
}
@Test
@Disabled
void test3() {
System.out.println("## test3 시작 ##\n");
}
}
실행 결과

컨트롤러 테스트
getProductTest 코드
test/controller/ProductControllerTest.java
package com.springboot.hello.controller;
import com.springboot.hello.dto.ProductResponseDto;
import com.springboot.hello.service.impl.ProductServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.BDDMockito.given;
@WebMvcTest(ProductController.class) //웹에서 사용되는 요청과 응답에 대한 테스트 수행, @SpringBootTest보다 가볍게 테스트 하기 취해 사용
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean // 실제 빈 객체가 아닌 가짜 객체를 생성해서 주입하는 역할, given() 메서드를 통해 동작을 정의해야 함
ProductServiceImpl productService;
@Test // 테스트 코드가 포함돼 있다고 선언하는 어노테이션, JUnit Jupiter에서 감지해서 테스트 계획에 포함시킴
@DisplayName("MockMvc를 통한 Product 데이터 가져오기 테스트")
void getProductTest() throws Exception {
given(productService.getProduct(123L)).willReturn(
ProductResponseDto.builder()
.number(123L)
.name("pen")
.stock(5000)
.price(2000)
.build()
);
String productId = "123";
mockMvc.perform(
get("/product?number="+productId))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
.andDo(print());
verify(productService).getProduct(123L);
}
}
결과

createProductTest 코드
data/dto/ProductDto.java
package com.springboot.hello.dto;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@AllArgsConstructor //Lombok 모든 인수를 받는 생성자 자동 생성
@Getter //Lombok Getter 클래스 자동 생성
@Setter //Lombok Setter 클래스 자동 생성
@EqualsAndHashCode //hashcode() : 두 객체의 내부의 값이 같은지 숫자로 확인 / equals() : 같은 객체인지 확인하는 메소드
public class ProductDto {
private String name;
private int price;
private int stock;
}
test/controller/ProductControllerTest.java
package com.springboot.hello.controller;
import com.google.gson.Gson;
import com.springboot.hello.dto.ProductDto;
import com.springboot.hello.dto.ProductResponseDto;
import com.springboot.hello.service.impl.ProductServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.mockito.BDDMockito.given;
@WebMvcTest(ProductController.class) //웹에서 사용되는 요청과 응답에 대한 테스트 수행, @SpringBootTest보다 가볍게 테스트 하기 취해 사용
public class ProductControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean // 실제 빈 객체가 아닌 가짜 객체를 생성해서 주입하는 역할, given() 메서드를 통해 동작을 정의해야 함
ProductServiceImpl productService;
@Test
@DisplayName("Product 데이터 생성 테스트")
void createProductTest() throws Exception {
//Mock 객체에서 특정 메서드가 실행되는 경우 실제 Return을 줄 수 없기 때문에 아래와 같이 가정 사항을 만들어줌
given(productService.saveProduct(new ProductDto("pen", 5000, 2000)))
.willReturn(ProductResponseDto.builder()
.number(123L)
.name("pen")
.price(5000)
.stock(2000)
.build()
);
ProductDto productDto = new ProductDto("pen", 5000, 2000);
Gson gson = new Gson();
String content = gson.toJson(productDto);
mockMvc.perform(
post("/product")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.name").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
.andDo(print());
verify(productService).saveProduct(new ProductDto("pen", 5000, 2000));
}
}
결과

서비스 테스트
getProductTest 소스
test/service/ProductServiceTest.java
package com.springboot.hello.service.impl;
import com.springboot.hello.data.dao.ProductDAO;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.dto.ProductResponseDto;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.verify;
public class ProductServiceTest {
private ProductDAO productDAO = Mockito.mock(ProductDAO.class);
private ProductServiceImpl productService;
@BeforeEach
public void setUpTest() {
productService = new ProductServiceImpl(productDAO);
}
@Test
void getProductTest() {
Product givenProduct = new Product();
givenProduct.setNumber(123L);
givenProduct.setName("펜");
givenProduct.setPrice(1000);
givenProduct.setStock(1234);
Mockito.when(productDAO.selectProduct(123L))
.thenReturn(givenProduct);
ProductResponseDto productResponseDto = productService.getProduct(123L);
Assertions.assertEquals(productResponseDto.getNumber(), givenProduct.getNumber());
Assertions.assertEquals(productResponseDto.getName(), givenProduct.getName());
Assertions.assertEquals(productResponseDto.getPrice(), givenProduct.getPrice());
Assertions.assertEquals(productResponseDto.getStock(), givenProduct.getStock());
verify(productDAO).selectProduct(123L);
}
}
saveProductTest 소스
test/service/ProductServiceTest.java
package com.springboot.hello.service.impl;
import com.springboot.hello.data.dao.ProductDAO;
import com.springboot.hello.data.entity.Product;
import com.springboot.hello.dto.ProductDto;
import com.springboot.hello.dto.ProductResponseDto;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
public class ProductServiceTest {
private ProductDAO productDAO = Mockito.mock(ProductDAO.class);
private ProductServiceImpl productService;
@BeforeEach
public void setUpTest() {
productService = new ProductServiceImpl(productDAO);
}
@Test
void saveProductTest() {
//any() : 모든 매개 변수(인수)에 대하여 같은 행동을 하는 Mock 객체를 생성
Mockito.when(productDAO.insertProduct(any(Product.class)))
.then(returnsFirstArg());
ProductResponseDto productResponseDto = productService.saveProduct(
new ProductDto("펜", 1000, 1234)
);
Assertions.assertEquals(productResponseDto.getName(), "펜");
Assertions.assertEquals(productResponseDto.getPrice(), 1000);
Assertions.assertEquals(productResponseDto.getStock(), 1234);
verify(productDAO).insertProduct(any());
}
}
데이터베이스 저장 테스트
saveTest 코드
data/repository/ProductRepositoryTestByMysql.java
package com.springboot.hello.data.repository;
import com.springboot.hello.data.entity.Product;
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.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
public class ProductRepositoryTestByMysql {
@Autowired
private ProductRepository productRepository;
@Test
void saveTest() {
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1234);
//when
Product savedProduct = productRepository.save(product);
//then
assertEquals(product.getName(), savedProduct.getName());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
selectTest 코드
data/repository/ProductRepositoryTestByMysql.java
package com.springboot.hello.data.repository;
import com.springboot.hello.data.entity.Product;
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.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
public class ProductRepositoryTestByMysql {
@Autowired
private ProductRepository productRepository;
@Test
void selectTest() {
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1234);
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());
}
}
updateTest 코드
data/repository/ProductRepositoryTestByMysql.java
package com.springboot.hello.data.repository;
import com.springboot.hello.data.entity.Product;
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.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
public class ProductRepositoryTestByMysql {
@Autowired
private ProductRepository productRepository;
@Test
void updateTest() {
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1234);
Product savedProduct = productRepository.saveAndFlush(product);
//when
Product foundProduct = productRepository.findById(savedProduct.getNumber()).orElseThrow(RuntimeException::new);
foundProduct.setName("장난감");
Product updateProduct = productRepository.save(foundProduct);
//then
assertEquals(updateProduct.getName(), "장난감");
}
}
deleteTest 코드
data/repository/ProductRepositoryTestByMysql.java
package com.springboot.hello.data.repository;
import com.springboot.hello.data.entity.Product;
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.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@DataJpaTest // 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행(환경은 내장/테스트는 물리 불일치 에러 발생)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 데이터소스를 내장으로 교체하지 않음
public class ProductRepositoryTestByMysql {
@Autowired
private ProductRepository productRepository;
@Test
void deleteTest() {
//given
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1234);
Product savedProduct = productRepository.saveAndFlush(product);
Product foundProduct = productRepository.findById(savedProduct.getNumber()).orElseThrow(RuntimeException::new);
//when
productRepository.delete(savedProduct);
//then
assertFalse(productRepository.findById(foundProduct.getNumber()).isPresent());
}
}