# Test

### 取得 profile 內的 server.port

當我們想要取得 yml 裡面設定的 port 時，會使用 `@Value("${server.port}")`

如果想要取得其他屬性，就修改大括號內的值，而在一般的屬性，都可以這樣直接取

但這裡面有一些隱藏的坑，像是取 port 的時候，會如下表

| yml \ code            | main | test |
| --------------------- | ---- | ---- |
| applicaition.yml      | O    | O    |
| application-local.yml | O    | X    |

這個表的意思是說，如果你直接把 `server.port` 寫在 `application.yml` 裡面，那很簡單，在 main 跟 test 裡面，都可以用上述方法直接取得。

但因為不同環境，會有相對應的配置文件，我們可以在 application.yml 只填寫

```
spring: 
  profiles: 
    active: local
```

而將 `local` 環境的配置，寫在 `application-local.yml` 這個配置文件裡

```
server: 
  port: 9999
```

這樣我們就可以根據不同的環境，在環境的 `application.yml` 去選擇對應的配置文件

但這樣當我們在測試程式裡，就沒辦法簡單的抓到 server.port 了

必須在我們的測試程式的類別上，加上這個註解

```
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
```

參考資料:springboot test @value（“$ {server.port}”）(<http://cn.voidcc.com/question/p-vkkqsybe-tz.html>)

## Conditional Test 根據註解的條件去決定是否執行測試

`@EnabledOnOs` 假如符合條件的作業系統，則執行這段測試&#x20;

```
@Test
@EnabledOnOs({OS.WINDOWS, OS.MAC})
public void test(){
    //...
}
```

{% hint style="info" %}
&#x20;Super-powers are granted randomly so please submit an issue if you're not happy with yours.
{% endhint %}

Once you're strong enough, save the world:

```
// Ain't no code for that yet, sorry
echo 'You got to trust me on this, I saved the world'
```

## MockMvc

參考資料:(<https://medium.com/chikuwa-tech-study/spring-boot-%E7%AC%AC9%E8%AA%B2-%E4%BD%BF%E7%94%A8mockmvc%E8%87%AA%E5%8B%95%E5%8C%96%E6%B8%AC%E8%A9%A6-%E4%B8%80-3e3d031f8d68>)

### 基本建置

```
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
```

注意 此範例為 spring boot 2.1.2

```
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ProductTest {

    @Autowired
    private MockMvc mockMvc;

}
```

當spring boot 改為2.4.3時，要改為以下寫法

```
@RunWith(SpringRunner.class)
改為
@ExtendWith(SpringExtension.class)
```

### 參數設定

測試程式和原本業務邏輯的程式一樣，也有自己的設定檔。\
因此請在src\test路徑下新增「resources」資料夾，建立「application.properties」檔案。\
並添加測試程式使用的資料庫專屬位址。請不要與開發中程式使用相同的資料庫，避免互相影響。

### 範例

```
@Test
public void testCreateProduct() throws Exception {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add("Content-Type", "application/json");

    JSONObject request = new JSONObject();
    request.put("name", "Harry Potter");
    request.put("price", 450);

    RequestBuilder requestBuilder =
            MockMvcRequestBuilders
                    .post("/products")
                    .headers(httpHeaders)
                    .content(request.toString());

    mockMvc.perform(requestBuilder)
            .andDo(print())
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").hasJsonPath())
            .andExpect(jsonPath("$.name").value(request.getString("name")))
            .andExpect(jsonPath("$.price").value(request.getInt("price")))
            .andExpect(header().exists("Location"))
            .andExpect(header().string("Content-Type", "application/json;charset=UTF-8"));
}
```

請求發出後，隨即透過「andExpect」方法進行回應資料的驗證。以下介紹在範例中使用到的驗證參數。

1. 「status().isCreated()」：驗證HTTP狀態碼應為201。讀者可自行探索其他狀態，如「isOk」、「isNotFound」等。或透過「is」方法直接傳入狀態碼。
2. 「jsonPath()」：指定JSON欄位。以「$」符號開始，使用「.」符號深入下一層的路徑。
3. 「hasJsonPath()」：驗證某個JSON欄位存在。
4. 「value()」：驗證某個JSON欄位值為何。
5. 「header().exists()」：驗證回應標頭中的某欄位存在。
6. 「header().string()」：驗證回應標頭中的某欄位值為何。

### Response 亂碼

![](https://460672175-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lmbdh9OrhI1G9B94HRH%2Fuploads%2F6SeT5n7J4gp4obssLEdR%2Fimage.png?alt=media\&token=4fdd2cc0-daae-444d-9198-1cb213cbb432)

```
protected ResultActions getResponseEncoding(RequestBuilder builder) throws Exception {
        ResultActions resultActions = mockMvc.perform(builder);
        resultActions
                .andReturn()
                .getResponse()
                .setCharacterEncoding("UTF-8");
        return resultActions;
}
```
