# Spring Boot JUnit 5

主要為實作JUnit5 實現單元測試，之前用比較多的都是JUnit4，這次會要使用JUnit5主要是因為Spring Boot 2.2.0 以上的版本 JUnit 都改為JUnit5 的版本，藉由此次專案來徹底玩一下單元測試。

以往在測試ＡＰＩ如果有使用Swagger 就可以直接在上面測試，但是如果要做到完整的CI/CD，撰寫單元測試的事情是不可或缺的，但因為很久沒有這樣徹底玩過，確實有很多地方是值得一玩，這邊就作為測試的紀錄～\
參考一: [前往](https://tw511.com/a/01/31070.html#Spring_BootMockMvc_54)\
參考二: [前往](https://www.jianshu.com/p/4648fd55830e)

```java
// JUnitController.java

import com.longxiang.backstage.controller.model.request.InsertMerchantReq;
import com.longxiang.backstage.controller.model.request.QueryMerchantReq;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * Author: Caster
 * Date: 2022/9/14
 * Comment:
 */
@RestController
@Api(tags = "JUnit5測試")
@RequestMapping("/junit")
@RequiredArgsConstructor
@Slf4j
public class JUnitController {

    @GetMapping("/list")
    public ResponseEntity list(@ModelAttribute QueryMerchantReq req){
        log.debug("====> list");
        return ResponseEntity.ok("list");
    }
    @GetMapping("/{id}")
    public ResponseEntity info(@PathVariable Integer id){
        log.debug("====> info");
        return ResponseEntity.ok("info");
    }

    @PostMapping("")
    public ResponseEntity insert(@RequestBody InsertMerchantReq req){
        log.debug("====> insert");
        return ResponseEntity.ok("insert");
    }

    @PutMapping("/{id}")
    public ResponseEntity update(@RequestBody InsertMerchantReq req){
        log.debug("====> update");
        return ResponseEntity.ok("update");
    }

    @DeleteMapping("/{id}")
    public ResponseEntity delete(@PathVariable Integer id){
        log.debug("====> delete");
        return ResponseEntity.ok("delete");
    }
}
```

```java

import com.longxiang.backstage.controller.model.request.InsertMerchantReq;
import com.longxiang.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * Author: Caster
 * Date: 2022/9/14
 * Comment:
 */
@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JUnitControllerTest {

    @Autowired
    private MockMvc mockMvc;
    public HttpHeaders httpHeaders = new HttpHeaders();

    @BeforeAll
    static void beforeAll() throws Exception {
        log.info("------ @BeforeAll ------");
    }

    @BeforeEach
    void beforeEach() throws Exception {
        log.info("------ @BeforeEach ------");
    }

    @AfterAll
    static void afterAll() throws Exception {
        log.info("------ @AfterAll ------");
    }

    @AfterEach
    void afterEach() throws Exception {
        log.info("------ @AfterEach ------");
    }

    @Test
    @Order(0)
    public void listTest() throws Exception {
        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
        param.add("name", "1");
        param.add("status", "1");
        var result =
                mockMvc.perform(
                                MockMvcRequestBuilders.get("/junit/list")
                                        .headers(httpHeaders)
                                        .params(param))
                        .andExpect(status().isOk())
                        .andDo(print())
                        .andReturn();
        System.out.println(result);
    }

    @Test
    @Order(1)
    public void infoTest() throws Exception {
        Integer id = 1;
        var result =
                mockMvc.perform(
                                MockMvcRequestBuilders.get("/junit/{id}", id)
                                        .headers(httpHeaders))
                        .andExpect(status().isOk())
                        .andDo(print())
                        .andReturn();
        System.out.println(result);
    }

    @Test
    @Order(2)
    public void insertTest() throws Exception {
        var insertInfo = new InsertMerchantReq();
        var result =
                mockMvc.perform(
                                MockMvcRequestBuilders.post("/junit")
                                        .headers(httpHeaders)
                                        .accept(MediaType.APPLICATION_JSON)
                                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                                        .content(JsonUtil.toJson(insertInfo)))
                        .andExpect(status().isOk())
                        .andDo(print())
                        .andReturn();
        System.out.println(result);
    }

    @Test
    @Order(3)
    public void updateTest() throws Exception {
        Integer id = 1;
        var insertInfo = new InsertMerchantReq();
        var result =
                mockMvc.perform(
                                MockMvcRequestBuilders.put("/junit/{id}", id)
                                        .headers(httpHeaders)
                                        .accept(MediaType.APPLICATION_JSON)
                                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                                        .content(JsonUtil.toJson(insertInfo)))
                        .andExpect(status().isOk())
                        .andDo(print())
                        .andReturn();
        System.out.println(result);
    }

    @Test
    @Order(4)
    public void deleteTest() throws Exception {
        Integer id = 1;
        var result =
                mockMvc.perform(
                                MockMvcRequestBuilders.delete("/junit/{id}", id)
                                        .headers(httpHeaders))
                        .andExpect(status().isOk())
                        .andDo(print())
                        .andReturn();
        System.out.println(result);
    }
}
```

```log
2022-09-14 16:58:08.970 INFO  controller.JUnitControllerTest - ------ @BeforeAll ------

2022-09-14 16:58:27.045 INFO  controller.JUnitControllerTest - ------ @BeforeEach ------
2022-09-14 17:05:54.559 DEBUG backstage.controller.JUnitController - ====> list
2022-09-14 16:58:27.915 INFO  controller.JUnitControllerTest - ------ @AfterEach ------

2022-09-14 17:05:54.626 INFO  controller.JUnitControllerTest - ------ @BeforeEach ------
2022-09-14 17:05:54.650 DEBUG backstage.controller.JUnitController - ====> info
2022-09-14 17:05:54.656 INFO  controller.JUnitControllerTest - ------ @AfterEach ------

2022-09-14 17:05:54.671 INFO  controller.JUnitControllerTest - ------ @BeforeEach ------
2022-09-14 17:05:54.810 DEBUG backstage.controller.JUnitController - ====> insert
2022-09-14 17:05:54.814 INFO  controller.JUnitControllerTest - ------ @AfterEach ------

2022-09-14 17:05:54.824 INFO  controller.JUnitControllerTest - ------ @BeforeEach ------
2022-09-14 17:05:54.836 DEBUG backstage.controller.JUnitController - ====> update
2022-09-14 17:05:54.839 INFO  controller.JUnitControllerTest - ------ @AfterEach ------

2022-09-14 17:05:54.852 INFO  controller.JUnitControllerTest - ------ @BeforeEach ------
2022-09-14 17:05:54.860 DEBUG backstage.controller.JUnitController - ====> delete
2022-09-14 17:05:54.863 INFO  controller.JUnitControllerTest - ------ @AfterEach ------

2022-09-14 16:58:28.306 INFO  controller.JUnitControllerTest - ------ @AfterAll ------
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xu-min-chang.gitbook.io/caster-develop-note/java/spring-boot/spring-boot-junit-5.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
