: 작성한 코드나 비즈니스 로직 자체를 테스트하기 위해 작성한 코드
📌 테스트 주도 개발(TDD)
: 테스트 코드를 더 잘 작성하고 활용할 수 있는지 고민한 결과로 등장한 애자일 방법론 중 하나
→ 테스트 코드를 작성하는 것과는 엄연히 다르지만 개발 관점을 다르게 볼 수 있는 기회가 될 수 있음
📌 V모델에서의 확인 단계
: 인수 테스트 > 시스템 테스트 > 통합 테스트 > 단위 테스트
: 애플리케이션의 개별 모듈을 독립적으로 테스트하는 방식
특징
: 애플리케이션을 구성하는 다양한 모듈을 결합해 전체적인 로직이 의도한 대로 동작하는지 테스트하는 방식
특징
: 테스트 코드 표현 방식 중 하나로 단계를 설정해서 각 단계의 목적에 맞게 코드를 작성함
특징
단계
: 테스트 코드를 작성하는 데 도움이 될 수 있는 5가지 규칙 의미
특징
규칙
📌 JUnit : 자바 언어에서 사용되는 대표적인 테스트 프레임워크
→ 단위 테스트를 위한 도구와 통합 테스트를 할 수 있는 기능 제공
특징
JUnit 5 기준
⇒ JUnit : 하나의 Platform 모듈을 기반으로 Jupiter와 Vintage 모듈이 구현체의 역할을 수행함
📌 6장에서 만들었던 코드 가져옴
→ DAO 레이어 제외하고 서비스 레이어에서 바로 리포지토리를 사용하는 구조로 수정해서 진행
*수정 코드
// service/impl/ProductServiceImpl.java
@Service
public class ProductServiceImpl implements ProductService {
private final Logger LOGGER = LoggerFactory.getLogger(ProductServiceImpl.class);
private final ProductRepository productRepository;
@Autowired
public ProductServiceImpl(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
public ProductResponseDto getProduct(Long number) {
LOGGER.info("[getProduct] input number : {}", number);
Product product = productRepository.findById(number).get();
LOGGER.info("[getProduct] product number : {}, name : {}", product.getNumber(), product.getName());
ProductResponseDto productResponseDto = new ProductResponseDto();
productResponseDto.setNumber(product.getNumber());
productResponseDto.setName(product.getName());
productResponseDto.setPrice(product.getPrice());
productResponseDto.setStock(product.getStock());
return productResponseDto;
}
@Override
public ProductResponseDto saveProduct(ProductDto productDto) {
LOGGER.info("[saveProduct] productDTO : {}", productDto.toString());
Product product = new Product();
product.setName(productDto.getName());
product.setPrice(productDto.getPrice());
product.setStock(productDto.getStock());
Product savedProduct = productRepository.save(product);
LOGGER.info("[saveProduct] savedProduct :: {}", savedProduct);
ProductResponseDto productResponseDto = new ProductResponseDto();
productResponseDto.setNumber(savedProduct.getNumber());
productResponseDto.setName(savedProduct.getName());
productResponseDto.setPrice(savedProduct.getPrice());
productResponseDto.setStock(savedProduct.getStock());
return productResponseDto;
}
@Override
public ProductResponseDto changeProductName(Long number, String name) {
Product foundProduct = productRepository.findById(number).get();
foundProduct.setName(name);
Product changedProduct = productRepository.save(foundProduct);
ProductResponseDto productResponseDto = new ProductResponseDto();
productResponseDto.setNumber(changedProduct.getNumber());
productResponseDto.setName(changedProduct.getName());
productResponseDto.setPrice(changedProduct.getPrice());
productResponseDto.setStock(changedProduct.getStock());
return productResponseDto;
}
@Override
public void deleteProduct(Long number) {
productRepository.deleteById(number);
}
}
// entity/Product.java
@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString(exclude = "name")
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long number;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer price;
@Column(nullable = false)
private Integer stock;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
spring-boot-starter-test
: 스프링 부트에서 테스트 환경을 쉽게 설정할 수 있도록 지원하는 프로젝트로 다양한 테스트 도구를 제공하고 자동 설정을 지원함
→ 사용을 위해 pom.xml 파일에 의존성 추가
</dependencies>
... 생략 ...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
... 생략 ...
</dependencies>
대표적인 라이브러리
// com.springboot.test/TestLifeCycle.java
import org.junit.jupiter.api.*;
public class TestLifeCycle {
@BeforeAll
static void beforeAll() {
System.out.println("## BeforeAll Annotation 호출 ##");
System.out.println();
}
@AfterAll
static void afterAll() {
System.out.println("## AfterAll Annotation 호출 ##");
System.out.println();
}
@BeforeEach
void beforeEach() {
System.out.println("## BeforeEach Annotation 호출 ##");
System.out.println();
}
@AfterEach
void afterEach() {
System.out.println("## AfterEach Annotation 호출 ##");
System.out.println();
}
@Test
void test1() {
System.out.println("## Test1 시작 ##");
System.out.println();
}
@Test
@DisplayName("Test Case 2!!")
void test2() {
System.out.println("## Test2 시작 ##");
System.out.println();
}
@Test
@Disabled
void test3() {
System.out.println("## Test3 시작 ##");
System.out.println();
}
}
/* 결과
## BeforeAll Annotation 호출 ##
## BeforeEach Annotation 호출 ##
## Test1 시작 ##
## AfterEach Annotation 종료 ##
## BeforeEach Annotation 호출 ##
## Test2 시작 ##
## AfterEach Annotation 종료 ##
## AfterAll Annotation 종료 ##
*/
테스트 순서에 관여하게 되는 대표적인 어노테이션
📌 컨트롤러 : 클라이언트로부터 요청을 받아 그에 걸맞은 서비스 컴포넌트로 요청을 전달하고 결괏값을 가공해 응답하는 역할
→ 여러 레이어 중 가장 웹에 가까이 있는 모듈
@RestController
@RequestMapping("/product")
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping()
public ResponseEntity<ProductResponseDto> getProduct(Long number) {
ProductResponseDto productResponseDto = productService.getProduct(number);
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto);
}
@PostMapping()
public ResponseEntity<ProductResponseDto> createProduct(@RequestBody ProductDto productDto) {
ProductResponseDto productResponseDto = productService.saveProduct(productDto);
return ResponseEntity.status(HttpStatus.OK).body(productResponseDto);
}
... 생략 ...
}
ProductController : ProductService의 객체를 의존성 주입받음. 즉, ProductService는 외부 요인에 해당함
⇒ 독립적인 테스트 코드 작성을 위해선 Mock 객체를 활용해야 함
ProductController의 getProduct()와 createProduct() 메서드 테스트 코드
→ test/com.spring.test/controller/ProductControllerTest.java 파일 생성
// test/com.spring.test/controller/ProductControllerTest.java
import static org.mockito.BDDMockito.given;
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;
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
@Autowired
// MockMvc : 컨트롤러의 구동 없이 가상의 MVC 환경에서 모의 HTTP 서블릿을 요청하는 유틸리티 클래스
private MockMvc mockMvc;
@MockBean
ProductServiceImpl productService;
@Test
@DisplayName("MockMvc를 통한 Product 데이터 가져오기 테스트")
void getProductTest() throws Exception {
// Given 부분
// given() : 객체에 호출되는 메서드와 주입되는 파라미터을 가정함, willReturn() : 리턴할 결과 정의
given(productService.getProduct(123L)).willReturn(
new ProductResponseDto(123L, "pen", 5000, 2000)
);
String productId = "123";
// When-Then 부분
/*
perform() : 서버로 URL 요청을 보낸 것처럼 통신 테스트 코드를 작성해서 컨트롤러를 테스트할 수 있음
=> MockMcvRequestBuilders에서 제공하는 GET, POST, PUT, DELETE로 URL을 정의해 사용
*/
mockMvc.perform(
get("/product?number=" + productId)).andExpect(status().isOk())
// andExpect() : perform() 메서드의 결괏값으로 리턴되는 ResultActions 객체를 검증함
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
// andDO() : 요청과 응답의 전체 내용 확인
.andDo(print());
// verify() : 지정된 메서드가 실행됐는지 검증하는 역할로 given()에 정의된 동작과 대응함
verify(productService).getProduct(123L);
}
@Test
@DisplayName("Product 데이터 생성 테스트")
void createProductTest() throws Exception {
// Mock 객체에서 특정 메서드가 실행되는 경우 실제 Return을 줄 수 없기 때문에 아래와 같이 가정 사항을 만들어줌
given(productService.saveProduct(new ProductDto("pen", 5000, 2000))).willReturn(
new ProductResponseDto(12315L, "pen", 5000, 2000)
);
ProductDto productDto = ProductDto.builder().name("pen").price(5000).stock(2000).build();
Gson gson = new Gson();
String content = gson.toJson(productDto);
mockMvc.perform(
// post() : 리소스 생성 기능을 테스트하기 위해 URL 구성, content() : DTO의 값을 담아 테스트 진행
post("/product").content(content).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
// jsonPath().exists() : POST 요청을 통해 도출된 결괏값에 대한 각 항목이 존재하는지 검증 -> 대응값이 없으면 오류 발생
.andExpect(jsonPath("$.number").exists())
.andExpect(jsonPath("$.price").exists())
.andExpect(jsonPath("$.stock").exists())
.andDo(print());
verify(productService).saveProduct(new ProductDto("pen", 5000, 2000));
}
}
📌 슬라이스 테스트 : @WebMvcTest 어노테이션을 사용한 테스트
특징 - 단위 테스트와 통합 테스트의 중간 개념으로 레이어드 아키텍처를 기준으로 각 레이어별로 나누어 테스트 진행함
→ 웹과 맞닿은 레이어인 컨트롤러는 외부 요인을 차단하면 테스트의 의미가 없기에 단위 테스트보다 슬라이스 테스트를 진행함
사용된 어노테이션
📌 Gson : 구글에서 개발한 JSON 파싱 라이브러리로 자바 객체와 JSON 문자열 간 변환을 하는 역할
-> 사용하기 위해 pom.xml 파일에 의존성 추가해야함
<dependencies>
... 생략 ...
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
... 생략 ...
<dependencies>
getProduct() 메서드와 saveProduct() 메서드 테스트 코드
// test/come.spring.test/impl/ProductServiceTest.java
public class ProductServiceTest {
// mock() : Mock객체로 ProductRepository 주입받음
private ProductRepository productRepository = Mockito.mock(ProductRepository.class);
private ProductServiceImpl productService;
@BeforeEach
public void setUpTest(){
productService = new ProductServiceImpl(productRepository);
}
@Test
void getProductTest(){
// Given 부분
// 엔티티 객체 생성 및 결괏값 리턴 설정
Product givenProduct = new Product();
givenProduct.setNumber(123L);
givenProduct.setName("펜");
givenProduct.setPrice(1000);
givenProduct.setStock(1234);
Mockito.when(productRepository.findById(123L)).thenReturn(Optional.of(givenProduct));
// ProductService의 getProduct() 메서드 호출해 동작 테스트
ProductResponseDto productResponseDto = productService.getProduct(123L);
// Assertion으로 리턴받은 ProductResponseDto 객체 검증
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(productRepository).findById(123L);
}
@Test
void saveProductTest(){
/* any() : Mock 객체의 동작을 정의하거나 검증하는 단계에서 메서드의 실행만 확인 or 클래스 객체를 매개변수로 전달받는 등의 상황에 사용
-> given()으로 정의된 Mock객체의 메서드 동작 감지는 매개변수 비교 or 레퍼런스 변수의 비교는 주솟값으로 이루어져
any()로 클래스만 정의하는 경우 존재
*/
Mockito.when(productRepository.save(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(productRepository).save(any());
}
}
+ Mock객체를 직접 생성하지 않고 @MockBean 어노테이션을 사용해 Mock 객체를 주입받는 방식
// JUnit5의 테스트에서 스프링 테스트 컨텍스트 사용하도록 설정
@ExtendWith(SpringExtension.class)
// @Autowired 어노테이션으로 주입받는 ProductService를 주입받기 위해 사용
@Import({ProductServiceImpl.class})
public class ProductServiceTest2 {
@MockBean
ProductRepository productRepository;
@Autowired
ProductService productService;
.. 생략 ..
}
Mock 객체 활용 방식 | @MockBean 어노테이션 사용 방식 | |
리포지터리 초기화 작업 | Mockito.mock() 사용 - 스프링 빈에 등록하지 않고 직접 객체를 초기화함 |
@MockBean 사용 - 스프링에 Mock 객체를 등록해 주입받음 |
차이점 | - 스프링 기능에 의존하는지 여부 - 테스트 속도에 큰 차이는 없지만 Mock 객체가 더 빠르게 동작함 |
📌 리포지토리 : 레이어 중 가장 DB에 가까움, JpaRepository를 상속받아 기본적인 쿼리 메서드 사용 가능
-> 구현하는 목적에 대해 고민해야함
고려 사항
... 생략 ...
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
... 생략 ...
DB에 값을 저장하고 조회하는 테스트 코드
// tet/com.springboot.test/data/repository/ProductRepositoryTestH2.java
@DataJpaTest
public class ProductRepositoryTestH2 {
@Autowired
private ProductRepository productRepository;
// 저장
@Test
void saveTest() {
//given - Product 엔티티 생서
Product product = new Product();
product.setName("펜");
product.setPrice(1000);
product.setStock(1000);
//when - 생성된 엔티티 기반으로 save() 메서드 호출해 테스트 진행
Product savedProduct = productRepository.save(product);
//then - 리턴 객체와 Given에서 생성한 엔티티 객체의 값이 일치하는지 검즌
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());
}
}
📌 @DataJpaTest 어노테이션
- JPA와 관련된 설정만 로드해 테스트 진행
- @Transactional 어노테이션을 포함 -> 테스트 코드 종료 시 자동으로 DB의 롤백 진행
- 임베디드 DB 사용 -> 다른 DB 사용하려면 별도의 설정 필요
임베디드 메모리 DB가 아닌 실제 사용하는 DB에서 테스트 진행
// test/com.springboot.test/data/repository/ProductRepositoryTest.java
@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.getNumber(), savedProduct.getNumber());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
📌 @AutoConfigureTestDatabase의 replace 요소 : @AutoConfigureTestDatabase의 값을 조정함
- 기본값 : Replace.ANY -> 임베디드 메모리 DB를 사용
- 실제 사용하는 DB로 테스트하기 위해선 Replace.NONE으로 변경
@SpringBootTest 어노테이션으로 테스트 수행
// test/com.springboot.test/data/repository/ProductRepositoryTestH2.java
@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 selectProduct = productRepository.findById(savedProduct.getNumber()).orElseThrow(RuntimeException::new);
// then
Assertions.assertThat(selectProduct.getNumber()).isEqualTo(givenProduct.getNumber());
Assertions.assertThat(selectProduct.getName()).isEqualTo(givenProduct.getName());
Assertions.assertThat(selectProduct.getPrice()).isEqualTo(givenProduct.getPrice());
Assertions.assertThat(selectProduct.getStock()).isEqualTo(givenProduct.getStock());
// >> update
// when
Product foundProduct = productRepository.findById(selectProduct.getNumber()).orElseThrow(RuntimeException::new);
foundProduct.setName("장난감");
Product updatedProduct = productRepository.save(foundProduct);
// then
assertEquals(updatedProduct.getName(), "장난감");
// >> delete
//when
productRepository.delete(updatedProduct);
// then
assertFalse(productRepository.findById(selectProduct.getNumber()).isPresent());
}
}
@SpringBootTest 특징
- 스프링의 모든 설정을 가져오고 빈 객체도 전체를 스캠함 -> 의존성 주입에 대한 고민할 필요 없이 테스트 가능
- 테스트 속도가 느림
📌 코드 커버리지 : 테스트 수준이 충분하지 표현하는 지표 중 하나로 대상 코드가 실행됐는지 표현하는 방법으로 사용
-> 가장 보편적인 도구 JaCoCo
JaCoCo : JUnity 테스트를 통해 얼마나 테스트됐는지 Line과 Branch를 기준으로 한 커버리지 리포트함
-> 런타임으로 테스트 케이스 실행하고 커버리지를 체크하는 방식, 리포트 - HTML, XML, CSV 같은 다양한 형식으로 확인 가능
pom.xml 파일에서 의존성 추가 및 플러그인 추가
... 생략 ...
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
</dependency>
... 생략 ...
<build>
... 생략 ...
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<configuration>
<excludes>
<exclude>**/ProductServiceImpl.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
<rule>
<element>METHOD</element>
<limits>
<limit>
<counter>LINE</counter>
<value>TOTALCOUNT</value>
<maximum>50</maximum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
: 플러그인으로 테스트 커버리지 측정하려면 메이븐의 테스트 단계가 선행되어야 함 - 생명주기는 Mayen탭에서 확인 가능
-> package 더블클릭해 빌드하면 target 폴더에 site>jacoco 폴더 생성됨
* 정상적으로 작동하지 않으면 경로에 한글이 없는지 확인
HTML 리포트 파일
각 칼럼
세부 내용 확인
1. controller 패키지를 누르면 ProductController 클래스가 Element에 표시됨
2. 해당 부분 다시 클릭 시 Method가 Elemet의 기준이 됨
3. 메서드 클릭 시 코드 레벨에서 테스트 커버리지 확인 가능
=> 초록색 : 테스트 실행 / 빨간색 : 실행되지 않은 라인 / 노랑색 : 조건문 중 하나만 테스트된 경우
테스트 주도 개발(TDD) : 테스트 코드 먼저 작성 후 테스트를 통과하는 코드를 작성하는 과정 반복하는 반복 테스트 개발 방법
-> 특징 - 애자일 방법론 중 하나인 익스트림 프로그랭의 Test-First 개념에 기반 => 개발 주기 짧은 단순 설계를 중시함
📌 애자일 소프트웨어 방법론
: 신속한 반복 작업을 통해 실제 작동 간으한 소프트웨어를 개발하는 개발 형식
: 3단계로 표현함
일반적으로 설계 후 그에 맞게 코드 작성하고 테스트 코드 작성하는 흐름
but 테스트 주도 개발은 설계 이후 바로 테스트 코드 작성하고 애플리케이션 코드를 작성함
1. ( 테스트 코드 )는 작성한 코드나 비즈니스 로직 자체를 테스트하기 위해 작성한 코드이다.
2. 단위 테스트(Unit Test)는 애플리케이션의 개별 모듈을 ( 독립적 )으로 테스트하는 방식이다.
3. ( Given-When-Then 패턴 )은 테스트 코드 표현 방식 중 하나로 단계를 설정해서 각 단계의 목적에 맞게 코드를 작성한다.
4. ( JUnit )이란, 자바 언어에서 사용되는 대표적인 테스트 프레임워크로 단위 테스트를 위한 도구와 통합 테스트를 할 수 있는 기능 제공한다.
5. ( 슬라이스 테스트 )란 @WebMvcTest 어노테이션을 사용한 테스트다.
6. Mock 객체 활용 방법과 @MockBean 어노테이션 활용 방법의 차이점은 ( 스프링 기능에 의존하는지 ) 여부이다.
7. ( 테스트 주도 개발 )란 테스트 코드 먼저 작성 후 테스트를 통과하는 코드를 작성하는 과정 반복하는 반복 테스트 개발 방법이다.
1. Mock객체를 직접 생성하지 않고 @MockBean 어노테이션을 사용해 Mock 객체를 주입받는 방식이다. 빈칸에 들어가야할 어노테이션을 작성하시오.
// 빈칸
// 빈칸
public class ProductServiceTest2 {
@MockBean
ProductRepository productRepository;
@Autowired
ProductService productService;
.. 생략 ..
}
정답 :
@ExtendWith(SpringExtension.class)
@Import({ProductServiceImpl.class})
public class ProductServiceTest2 {
@MockBean
ProductRepository productRepository;
@Autowired
ProductService productService;
.. 생략 ..
}
2. 임베디드 메모리 데이터베이스가 아닌 실제로 사용하는 데이터베이스에서 테스트를 진행하기 위해서는 어떤 어노테이션을 추가해야하는지 빈칸에 알맞은 답을 작성하시오.
@DataJpaTest
// 빈칸
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.getNumber(), savedProduct.getNumber());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
정답 :
@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.getNumber(), savedProduct.getNumber());
assertEquals(product.getPrice(), savedProduct.getPrice());
assertEquals(product.getStock(), savedProduct.getStock());
}
}
[출처] 장정우, 『스프링 부트 핵심 가이드』, 위키북스(2022), p.159-207.
Corner Spring 1
Editor: Luna
[스프링 1팀] 6장 데이터베이스 연동 (0) | 2024.11.29 |
---|---|
[스프링 1팀] 5장~6.5장. API 작성과 데이터베이스 연동 (0) | 2024.11.22 |
[스프링 1팀] 1장~4장. 스프링 부트 개발 환경과 애플리케이션 개발하기 (0) | 2024.11.15 |
[스프링 1팀] 스프링 입문 섹션 7~8 (0) | 2024.11.08 |
[스프링 1팀] 스프링 입문 섹션 5~6 (0) | 2024.10.11 |