테스트 코드 작성

코딩하는 하늘토끼·2023년 6월 21일

스프링부트 공부

목록 보기
11/15

프로젝트 환경

종류환경
IDEIntellij IDEA 2023.1.2 (Ultimate Edition)
언어SpringBoot 3.1.0
타입Gradle - Groovy
JDKcorretto-17
패키지 생성Jar
버전관리Github

의존성

종류이름
WebSpring web
Developer ToolsSpring Processor / Lombok
SQLSpring 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());
    }


}

0개의 댓글