스프링부트 JUnit으로 테스트 2. REST API CRUD 테스트 코드 작성

min seung moon·2021년 7월 1일
0

Spring

목록 보기
42/50

1. Prject Test

  • Spring Intializr / web, lomboc / spring-calculator

01. 이전 포스팅에서 작성한 Clss 사용 및 어노테이션 추가

  • Package : component
  • Class : Calculator, MarketApi, DollorCalculator
  • Interface : ICalculator
  • ICalculator.interface
package com.example.springcalculator.component;

public interface ICalculator {

    int sum(int x, int y);
    int minus(int x, int y);
}
  • Calculator.java
package com.example.springcalculator.component;

import org.springframework.stereotype.Component;

@Component
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 mius(int x, int y) {
        return this.iCalculator.minus(x, y);
    }


}
  • MarketApi.java
package com.example.springcalculator.component;

import org.springframework.stereotype.Component;

// MarketApi가 주입을 받는 경우일 경우 MarketApi도 Component가 되어야 한다
@Component
public class MarketApi {

    public int connect() {
        // naver
        // kakao
        return 1100;
    }
}
  • DollorCalculator.java
package com.example.springcalculator.component;

import org.springframework.stereotype.Component;

@Component
public class DollorCalculator implements ICalculator {

    private int price = 1;
    private MarketApi marketApi;

    // MarketApi가 주입을 받는 경우일 경우 MarketApi도 Component가 되어야 한다
    public DollorCalculator(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;
    }
}

02. Controller 생성 및 Controller에 맞게 수정

  • Package : controller
  • Class : CalculatorApiController
  • CalculatorApiController.java
package com.example.springcalculator.controller;

import com.example.springcalculator.component.Calculator;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@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);
    }

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

-1. lombok을 사용하여 생성자 주입을 @RequiredArgsConstructor로 리펙토링하기

  • component / ICalculator.interface
    • init() method 추가
package com.example.springcalculator.component;

public interface ICalculator {
    void init();
    int sum(int x, int y);
    int minus(int x, int y);
}
  • component / Calculator.java
    • iCalculator를 final로 만들어서 @RequiredArgsConstructor 적용
package com.example.springcalculator.component;

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

@Component
@RequiredArgsConstructor
public class Calculator {

    private final ICalculator iCalculator;

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

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


}
  • component / DollorCalculator.java
    • MarketApi를 final로 만들어 @RequiredArgsConstructor 적용
package com.example.springcalculator.component;

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

@Component
@RequiredArgsConstructor
public class DollorCalculator implements ICalculator {

    private int price = 1;
    private final 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;
    }
}

-2. 서버 실행하여 테스트

-3. 서버 실행없이 테스트

  • test 폴더를 활용하며 main 폴더랑 패키지랑 파일 위치가 일치해야 한다

  • test도 서버처럼 동작을 한다

  • @SpringBootTest

    • 모든 Bean을 올리는 통합 테스트
  • DollorCalculatorTest.java

    • @MockBean
      • Spirng에서는 Mock이 아닌 MockBean을 사용하는데 그건 Spring Bean으로 관리 되기 때문이다
package com.example.springcalculator.component;

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 
//@Import({MarketApi.class, DollorCalculator.class})
// @SpringBootTest를 해주면 모든 객체들이 Bean에 할당이 되기에 import 안해줘도 된다
@SpringBootTest
public class DollorCalculatorTest {

    // Spirng에서는 Mock이 아닌 MockBean을 사용하는데 그건 Spring Bean으로 관리 되기 때문이다
    @MockBean
    private MarketApi marketApi;

    @Autowired
    private DollorCalculator dollorCalculator;

    @Test
    public void dollorCalculatorTest() {
        Mockito.when(marketApi.connect()).thenReturn(3000);
        dollorCalculator.init();

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

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

    }
}

-4. 단위 테스트(controller) - GET

  • controller / CalculatorApiControllerTest.java
    • @WebMvcTest(CalculatorApiController.class)
      • web test(test 할 클래스)를 위한 어노테이션
    • @AutoConfigureWebMvc
      • MVC와 관련된 Bean을 올린다
package com.example.springcalculator.controller;

import com.example.springcalculator.component.Calculator;
import com.example.springcalculator.component.DollorCalculator;
import com.example.springcalculator.component.MarketApi;
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.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
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(test 할 클래스)
@WebMvcTest(CalculatorApiController.class)
@AutoConfigureWebMvc
@Import({Calculator.class, DollorCalculator.class})
public class CalculatorApiControllerTest {

    @MockBean
    private MarketApi marketApi;

    // mvc를 모킹으로 테스트하겠다
    @Autowired
    private MockMvc mockMvc;

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

    @Test
    public void sumTest() throws Exception {
        // http://localhost:8080/api/sum?x=&y=

        mockMvc.perform(
                MockMvcRequestBuilders.get("http://localhost:8080/api/sum")
                        .queryParam("x","10")
                        .queryParam("y", "10")
        ).andExpect(
                MockMvcResultMatchers.status().isOk()
        ).andExpect(
                MockMvcResultMatchers.content().string("60000")
        ).andDo(MockMvcResultHandlers.print());
    }

}

-5. 단위 테스트(controller) - POST

  • main에 dto / Req, Res 추가
  • main / dto / Req.java
package com.example.springcalculator.dto;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Req {

    private int x;
    private int y;
}
  • main / dto/ Res.java
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
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Body {
        private String resultCode = "OK";
    }
}
  • main / controller / CalculatorApiController.java에 minus를 post로 수정
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.mius(req.getX(), req.getY());

        Res res = new Res();
        res.setResult(result);
        res.setResponse(new Res.Body());
        return res;
    }
}
  • test / controller / CalculatorApiControllerTest.java
package com.example.springcalculator.controller;

import com.example.springcalculator.component.Calculator;
import com.example.springcalculator.component.DollorCalculator;
import com.example.springcalculator.component.MarketApi;
import com.example.springcalculator.dto.Req;
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.WebMvcTest;
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(test 할 클래스)
@WebMvcTest(CalculatorApiController.class)
@AutoConfigureWebMvc
@Import({Calculator.class, DollorCalculator.class})
public class CalculatorApiControllerTest {

    @MockBean
    private MarketApi marketApi;

    // mvc를 모킹으로 테스트하겠다
    @Autowired
    private MockMvc mockMvc;

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

    @Test
    public void sumTest() throws Exception {
        // http://localhost:8080/api/sum?x=&y=

        mockMvc.perform(
                MockMvcRequestBuilders.get("http://localhost:8080/api/sum")
                        .queryParam("x","10")
                        .queryParam("y", "10")
        ).andExpect(
                MockMvcResultMatchers.status().isOk()
        ).andExpect(
                MockMvcResultMatchers.content().string("60000")
        ).andDo(MockMvcResultHandlers.print());
    }

    @Test
    public void minusTest() throws Exception {
        Req req = new Req();
        req.setX(10);
        req.setY(10);

        String json = new ObjectMapper().writeValueAsString(req);

        mockMvc.perform(
                MockMvcRequestBuilders.post("http://localhost:8080/api/minus")
                .contentType(MediaType.APPLICATION_JSON)
                .content(json)
        ).andExpect(
                MockMvcResultMatchers.status().isOk()
        ).andExpect(
                MockMvcResultMatchers.jsonPath("$.result").value("0")
        ).andExpect(
                // 뎁스 접근
                MockMvcResultMatchers.jsonPath("$.response.resultCode").value("OK")
        ).andDo(MockMvcResultHandlers.print());
    }

}

profile
아직까지는 코린이!

0개의 댓글