사이트

https://woowabros.github.io/experience/2020/02/02/jacoco-config-on-gradle-project.html

 

Gradle 프로젝트에 JaCoCo 설정하기 - 우아한형제들 기술 블로그

안녕하세요. 상품시스템팀에서 서버 개발(..새발)을 하고 있는 연철입니다.프로젝트 세팅 중에 찾아보고 삽질했던 내용들이 도움이 될까 하여 남깁니다.

woowabros.github.io

 

Jacoco

Java코드의 코드 커버리지를 체크하는 라이브러리 결과를 html, xml, csv로 확인이 가능하다 

 

Gradle에 plugins 항목에 id 추가

plugins {
    id 'org.springframework.boot' version '2.5.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'jacoco'
}

 

확인

 

테스트를 진행 (TEST를 더블 클릭한다.)

만약 test에 실패하였다면 아래와 같이 나오고

 

아래 파일을 실행 시키면(build -> reports -> tests -> test -> index.html)

 

인터넷으로 여는 것을 선택하면

 

에러난 결과를 확인 할 수 있다.

 

Test Report를 확인하는 방법

Gradle에서 jacocoTestReport를 더블 클릭해준다.

build -> reports -> jacoco -> test -> html -> index.html을 클릭한다.

인터넷으로 열어준다.

 

어떤 코드를 테스트 진행했는지 확인 할 수 있다.

'Spring > JUnit' 카테고리의 다른 글

Spring에서 JUnit 테스트 진행 - REST API CRUD 테스트  (0) 2021.06.27
JUnit  (0) 2021.06.27

MockMvc를 이용하여 Controller Test를 진행한다.

MockMvc: 테스트에 필요한 기능만 가지는 가짜 객체를 만들어서 애플리케이션 서버에 배포하지 않고

스프링 MVC 동작을 재현 할 수 있는 클래스

 

perform(): 원하는 요청을 전송하는 역할

 

andDo(print()): 요청/응답 전체 메세지를 확인할 수 있다.

 

@MockBean: 해당 클래스를 Mock 처리하고 스프링에서 bean으로 등록하여 사용 위해서 선언

 

@SpringBootTest: 모든 bean이 등록되어 사용할 때 선언, 전체 테스트를 진행할 때 사용

 

https://shinsunyoung.tistory.com/52

 

SpringBoot의 MockMvc를 사용하여 GET, POST 응답 테스트하기

안녕하세요! 이번 포스팅에서는 SpringBoot 프레임워크에서 제공해주는 MockMvc를 만들고 테스트하는 방법을 알아보도록 하겠습니다. 전체 코드는 Github에서 확인이 가능합니다. ✍️ 📚 개념 정리

shinsunyoung.tistory.com

 

테스트를 진행하기 위한 코드 작성

저번 포스트에서 작성한 calculator 프로젝트를 이용한다.

 

ICalculator 인터페이스 작성

package com.example.springcalculator.component;

public interface ICalculator {

    int sum(int x, int y);
    int minus(int x, int y);
    void init();
}

 

Calculator 클래스 작성

@RequiredArgsConstructor:  final로 선언된 변수의 생성자를 만들어 준다. 따라서 기존 생성자를 지워준다.

package com.example.springcalculator.component;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
// final로 선언된 변수의 생성자를 만들어 준다.
@RequiredArgsConstructor
public class Calculator {

    private final ICalculator iCalculator;

//    public Calculator(ICalculator iCalculator){
//        this.iCalculator = iCalculator;
//    }

    public int sum(int x, int y){
        // 서버로부터 환율을 받아온다.
        iCalculator.init();
        return this.iCalculator.sum(x,y);
    }

    public  int minus(int x, int y){
        // 서버로부터 환율을 받아온다.
        iCalculator.init();
        return iCalculator.minus(x, y);
    }
}

 

DollarCalculator 클래스 작성

@RequiredArgsConstructor:  final로 선언된 변수의 생성자를 만들어 준다. 따라서 기존 생성자를 지워준다.

package com.example.springcalculator.component;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
// final로 선언된 변수의 생성자를 만들어 준다.
@RequiredArgsConstructor
public class DollarCalculator implements ICalculator {

    private int price = 1;
    private final MarketApi marketApi;

//  현재 환율 가져오기 (생성자를 이용하여 클래스 호출시 자동)
//    public DollarCalculator(MarketApi marketApi){
//        this.marketApi = marketApi;
//    }
//  현재 환율을 금액에 대입 시킨다.
    @Override
    public void init(){
        this.price = marketApi.connect();
    }


    @Override
    public int sum(int x, int y) {
        x *= price;
        y *= price;

        return x + y;
    }

    @Override
    public int minus(int x, int y) {
        x *= price;
        y *= price;

        return x - y;
    }
}

 

MarketApi 클래스 작성 (환율 값 가져오는 클래스)

package com.example.springcalculator.component;

import org.springframework.stereotype.Component;

@Component
public class MarketApi {

    public int connect(){
        return 1100;
    }
}

 

POST 방식의 controller 테스트를 진행하기 위한 DTO 생성

Req 클래스 생성 (파라미터를 받을 DTO)

package com.example.springcalculator.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Req {

    private int x;
    private int y;
}

 

Res 클래스 작성 (연산 결과와 상태값을 저장할 DTO)

package com.example.springcalculator.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Res {

    private int result;

    private Body response;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Body{
        private String resultCode = "OK";
    }
}

 

controller 작성 (POST, GET 처리)

package com.example.springcalculator.controller;

import com.example.springcalculator.component.Calculator;
import com.example.springcalculator.dto.Req;
import com.example.springcalculator.dto.Res;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class CalculatorApiController {

    private final Calculator calculator;

    @GetMapping("/sum")
    public int sum(@RequestParam int x, @RequestParam int y){
        return calculator.sum(x, y);
    }

    @PostMapping("/minus")
    public Res minus(@RequestBody Req req){
        int result = calculator.minus(req.getX(), req.getY());

        Res res = new Res();
        res.setResult(result);
        // resultCode":"OK" 데이터 추가
        res.setResponse(new Res.Body());

        return res;
    }
}

 

JUnit Test 진행할 code 작성

기본적인 Test Code

package com.example.springcalculator;

import com.example.springcalculator.component.Calculator;
import com.example.springcalculator.component.DollarCalculator;
import com.example.springcalculator.component.MarketApi;
import com.example.springcalculator.dto.Req;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
public class DallarCalculatorTest {

    // 스프링에서는 Bean 처리되기 때문에
    @MockBean
    private MarketApi marketApi;

    @Autowired
    private Calculator calculator;

    @Test
    public void dollarCalculatorTest(){
        Mockito.when(marketApi.connect()).thenReturn(3000);

        int sum = calculator.sum(10, 10);
        int minus = calculator.minus(10, 10);

        Assertions.assertEquals(60000, sum);
        Assertions.assertEquals(0, minus);
    }

}

 

Controller를 이용하여 MVC 테스트를 진행할 코드 작성

package com.example.springcalculator.controller;

import com.example.springcalculator.component.Calculator;
import com.example.springcalculator.component.DollarCalculator;
import com.example.springcalculator.component.MarketApi;
import com.example.springcalculator.dto.Req;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

// web을 test하기 때문에 선언
// SpringBootTest을 이용하여 진행해도 되지만 SpringBootTest는 bean에 등록된 모든 자원을 이용하고
// WebMvcTest는 해당 클래스만 이용하기 때문에 자원을 아낄 수 있다.
@WebMvcTest(CalculatorApiController.class)
// MVC와 관련된 Bean을 올린다
@AutoConfigureWebMvc
// 테스트를 진행할 controller에서 사용하기 때문에 해당 클래스를 주입해준다.
@Import({Calculator.class, DollarCalculator.class})
public class CalculatorApiControllerTest {

    @MockBean
    private MarketApi marketApi;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    public void init(){
        Mockito.when(marketApi.connect()).thenReturn(3000);
    }

    // get 방식 테스트
    @Test
    public void sumTest() throws Exception {
        // http://localhost:8080/api/sum
        // 해당 URL로 접속
        mockMvc.perform(
                MockMvcRequestBuilders.get("http://localhost:8080/api/sum")
                        // 파라미터 값 추가
                .queryParam("x", "10")
                .queryParam("y", "10")
                // 기대하는 값 입력
        ).andExpect(
                // 200 ok
                MockMvcResultMatchers.status().isOk()
        ).andExpect(
                // 합: 60000
                MockMvcResultMatchers.content().string("60000")
                // 실행
        ).andDo(MockMvcResultHandlers.print());
    }

    // POST 방식 테스트
    @Test
    public void minusTest() throws Exception {

        Req req = new Req();
        req.setX(10);
        req.setY(10);
        // req를 json 형태로 변환
        String json = new ObjectMapper().writeValueAsString(req);
        System.out.println(json);
        
        // 해당 URL로 접속
        mockMvc.perform(
                MockMvcRequestBuilders.post("http://localhost:8080/api/minus")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
          // 기대하는 값 입력      
        ).andExpect(
                MockMvcResultMatchers.status().isOk()
            
        ).andExpect(
                // json의 result 항목이 0으로 나오는지 확인
                MockMvcResultMatchers.jsonPath("$.result").value("0")
        ).andExpect(
                // json의 response.resultCode 항목이 OK으로 나오는지 확인
                MockMvcResultMatchers.jsonPath("$.response.resultCode").value("OK")
                // 실행           
        ).andDo(MockMvcResultHandlers.print());
    }
}

 

실행 화면

get 방식 test

post 방식 test

 

 

 

'Spring > JUnit' 카테고리의 다른 글

jacoco를 이용하여 테스트 커버리지 확인하는 방법  (0) 2021.06.27
JUnit  (0) 2021.06.27

TDD(Test-Driven Development)

테스트 주도 개발에서 사용하지만, 코드의 유지 보수 및 운영 환경에서의 에러를 미리 방지 하기 위해서 단위 별로 검증하는 테스트 프레임워크

 

단위 테스트

작성한 코드가 기대하는 대로 동작을 하는지 검증 하는 절차

 

JUnit

Java기반의 단위 테스트를 위한 프레임워크

Annotation 기반으로 테스트를 지원하며, Assert를 통하여, (예상, 실제)를 통해 검증

 

 

실습 환경: Gradle 자바 프로젝트로 진행

해당 dependency, test가 존재하는지 확인

 

테스트를 진행하기 위한 계산 코드 작성

ICalculator 인터페이스 생성

public interface ICalculator {

    int sum(int x, int y);
    int minus(int x, int y);
}

원화 계산을 하기위한 KrwCalculator 클래스 생성

public class KrwCalculator implements ICalculator{

    private int price = 1;

    @Override
    public int sum(int x, int y) {
        x *= price;
        y *= price;

        return x + y;
    }

    @Override
    public int minus(int x, int y) {
        x *= price;
        y *= price;

        return x - y;
    }
}

달러 계산을 하기위한 DollarCalculator 클래스 생성

public class DollarCalculator implements ICalculator{

    private int price = 1;
    private MarketApi marketApi;

//  현재 환율 가져오기 (생성자를 이용하여 클래스 호출시 자동)
    public DollarCalculator(MarketApi marketApi){
        this.marketApi = marketApi;
    }
//  현재 환율을 금액에 대입 시킨다.
    public void init(){
        this.price = marketApi.connect();
    }


    @Override
    public int sum(int x, int y) {
        x *= price;
        y *= price;

        return x + y;
    }

    @Override
    public int minus(int x, int y) {
        x *= price;
        y *= price;

        return x - y;
    }
}

 

환율 값을 가져올 MarketApi 클래스 생성

public class MarketApi {

    public int connect(){
        return 1100;
    }
}

 

원화 클래스와 달러 클래스를 주입받기 위한 Calculator 클래스 선언

public class Calculator {

    private ICalculator iCalculator;

    public Calculator(ICalculator iCalculator){
        this.iCalculator = iCalculator;
    }

    public int sum(int x, int y){
        return this.iCalculator.sum(x,y);
    }

    public  int minus(int x, int y){
        return iCalculator.minus(x, y);
    }
}

 

테스트 코드 작성 (어노테이션 이용)

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

// Mock 어노테이션을 사용하기 위해 선언
@ExtendWith(MockitoExtension.class)
public class DollarCalculatorTest {

    //@Test
    public void testHello(){
        System.out.println("Hello");
    }

    // 테스트를 진행하려는 클래스 지정
    @Mock
    public MarketApi marketApi;

    // 원하는 클래스에 메소드가 동작할때 사용자 임의로 데이터를 설정할 수 있다.
    @BeforeEach
    public void init(){
        Mockito.lenient().when(marketApi.connect()).thenReturn(3000);
    }

    @Test
    public void dollarTest(){
        MarketApi marketApi = new MarketApi();

        DollarCalculator dollarCalculator = new DollarCalculator(marketApi);
        dollarCalculator.init();

        Calculator calculator = new Calculator(dollarCalculator);

        System.out.println(calculator.sum(10, 10));

        // Assertions.assertEquals("예상하는 데이터", "실제 동작하는 데이터")
        Assertions.assertEquals(22000, calculator.sum(10,10));
        Assertions.assertEquals(0, calculator.minus(10,10));
    }

    // Mock 어노테이션을 이용한 테스트 진행 함수
    @Test
    public void MockTest(){

        DollarCalculator dollarCalculator = new DollarCalculator(marketApi);
        dollarCalculator.init();


        Calculator calculator = new Calculator(dollarCalculator);
        Calculator calculatorK = new Calculator(new KrwCalculator());

        System.out.println(calculator.sum(10, 10));

        // Assertions.assertEquals("예상하는 데이터", "실제 동작하는 데이터")
        Assertions.assertEquals(60000, calculator.sum(10,10));
        Assertions.assertEquals(0, calculator.minus(10,10));

        Assertions.assertEquals(20, calculatorK.sum(10,10));
        Assertions.assertEquals(0, calculatorK.minus(10,10));
    }
}

예상한 데이터와 실제 데이터가 다를 경우 콘솔 화면

 

데이터가 일치할 경우 콘솔 화면

사용한 어노테이션

@ExtendWith(MockitoExtension.class) Mock 어노테이션을 사용하기 위해 선언
@Test 테스트를 진행할 메소드에 선언
@Mock 테스트를 진행할 때 사용할 클래스에 선언
@BeforeEach 원하는 클래스에 메소드가 동작할때 사용자 임의로 데이터를 설정할 수 있다.

Mock 어노테이션을 사용하기위해서 디펜던시를 추가로 등록해주어야 한다.

MvnRepository에서 Mockito 검색

https://mvnrepository.com/artifact/org.mockito/mockito-core/3.11.2

https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter/3.11.2

 

참고 사이트

https://javacan.tistory.com/entry/MocktestUsingMockito

 

Mockito를 이용한 MockTest

필자는 원하는 기능을 제공하는 Mock 라이브러리를 찾지 못해 (또는 완전히 학습하지 못해?) 필요한 기능을 제공하는 간단한 라이브러리를 직접 만들어서 사용한 적도 있다. 그런데, 매우 맘에

javacan.tistory.com

 

+ Recent posts