# 02.8-WebServic and APIs-Lesson8: Testing
###### tags: `Udacity`
[ToC]
# 01 Introduction
{%youtube UEIb-ohnLW0%}
# 02 Overview
{%youtube UVRpUx5zuIs%}
Testing gives you confidence that your application is working when you make changes, so you’ll want to introduce automated testing and make testing a normal part of your development process. First, unit test your small piece, then integrate your piece with the other system components to conduct integration testing.
**Dependency**
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
```
`Spring-boot-starter-test` imports both Spring Boot test modules as well as `JUnit`, `AssertJ`, `Hamcrest`, and a number of other useful libraries.
# 03 Unit Testing
{%youtube X4EQiHQC-_I%}
We will first look at unit testing REST APIs using [JUnit](https://junit.org/junit5/), [Mockito](https://site.mockito.org/) and [Spring Test](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html) (also known as, MockMVC).
* JUnit is a popular unit testing framework that allows you to test individual units of source code.
* Mockito is a great mocking framework which provices data for JUnit tests.
* `@MockBean` works well with the Mockito library.
* `@WebMvcTest` is used for controller layer unit testing.
# 04 Case Study: Unit Testing
{%youtube gAUx-H2VTLs%}
The code can be cloned from [GitLab](https://gitlab.com/videolearning/udacity-java/tree/master/Lesson8-testing).
**REST API Components**
* Controller - LocationController.java
* Service - LocationService.java and LocationServiceImpl.java
* CRUD Repository - LocationRepository.java
* Domain Entity/Model - Location.java
* H2 Database accessible via http://localhost:8080/h2/
* Tomcat Server accessible via http://localhost:8080
**Unit Testing Components**
* LocationControllerUnitTest.java
**Integration Testing Components**
* LocationControllerIntegrationTest.java
---
==ShannonNote==
* Controller 的UnitTest
* 首先在`/main/test`新增一個`web` package,在這個package創造`LocationControllerUnitTest.java`
* `LocationControllerUnitTest.java`新增`@RunWith(SpringRunner.class)`標籤
* 這個會去定義the runner class被使用來測試案例
* 使用SpringRunner因為我們使用Spring to build the application
* `LocationControllerUnitTest.java`新增`@WebMvcTest(LocationController.class)`標籤
* 該標籤的參數放你想要測試的controller
* 新增UnitTest class的屬性`@Autowired private MockMvc mockMvc`
* MockMvc不用去開始一個完整的HTTP server就可以快速地完成測試
* 新增UnitTest class的屬性`@MockBean LocationService locationService`
* 目的在mocking the location service bean,我們將LocationService新增該標籤是因為LocationController依賴於這個Service因此我們需要模擬Service
* 新增測試@Test方法
```java
@Test
public void getAllLocations() throws Exception {
//get代表getRequest
mockMvc.perform(get("/location/"))
//以下三行 可以設定Expectation on HTTP responses received from the controller class
//期望status returned to be Okay
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
//期望回傳的是json
.andExpect(content().json("[]"));
//它僅用於驗證已調用模擬方法的次數
verify(locationService, times(1)).retrieveLocations();
}
@Test
public void getLocation() throws Exception {
mockMvc.perform(get("/location/1"))
.andExpect(status().isOk());
verify(locationService, times(1)).retrieveLocation(1);
}
@Test
public void getLocation() throws Exception {
//使用perform()去模擬Call getRequest
mockMvc.perform(get("/location/1"))
.andExpect(status().isOk());
verify(locationService, times(1)).retrieveLocation(1);
}
```
# 05 Integration Testing
{%youtube LXOIVFISNmE%}
Integration testing allows for testing of the entire application (and all of its layers) as opposed to just individual components.
The `@SpringBootTest` annotation is useful for integration testing and is chosen over `@WebMvcTest` because `@SpringBootTest` starts the full application context (including the server) and does not customize component scanning at all.
`@SpringBootTest` will look for the main configuration class, annotated with`@SpringBootTest` and use that to start a Spring application context that simulates a calling client.
# 06 Case Study: Integration Testing
{%youtube NuFStrYCTp4%}
==ShannonNote==
* All Application 的Integration Test
* 首先在`/main/test`新增一個`web` package,在這個package創造`LocationControllerIntegrationTest.java`
* LocationControllerIntegrationTest.java
* `新增`@RunWith(SpringRunner.class)`標籤
* 新增`@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)`
* `SpringBootTest.WebEnvironment.RANDOM_PORT`: This starts the server with a random port and help to avoid conflicts in a test environment
* 新增`@AutoConfigureMockMvc`
* 這個標籤會模擬calling the code from the
* 新增屬性
* `@LocationServerPort private int port`
* 取抓取random port
* `@Autowired private TestRestTemplate restTemplate`
* 新增這個標籤之後 Spring boot 會自動提供TestRestTemplate,TestRestTemplate允許我們去測試REST APIs的情況projrammatically
* 新增@Test方法
```java
@Test
public void getAllLocations() {
ResponseEntity<List> response =
this.restTemplate.getForEntity("http://localhost:" + port + "/location/", List.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
@Test
public void getLocation() {
ResponseEntity<Location> response =
this.restTemplate.getForEntity("http://localhost:" + port + "/location/1", Location.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
```
# 07 Lab: Test Your Dog API
**Lab: Test Your Dog API**
Reuse your Dog REST API code from Lesson 2 and create unit and integration tests for it.
* Step 1: Create a unit test for your API using the `@WebMvcTest` annotation.
* Step 2: Create an integration test for your API using the `@SpringBootTest` annotation.
We haven't included a solution for this lab due to the many different tests you could possibly add for your API, but make sure to check back in the Case Studies if you need more ideas! You'll also get a chance to get creative in the upcoming project for what tests you want to implement.
# 08 Recap
{%youtube SVNNtbgbOtI%}
# 09 An Example on Testing & D..
{%youtube uKgTF5whmtU%}
# 10 Course Outro
{%youtube qQISqzsE6HY%}
# 結論
## 1. JUnit Test for RESTful APIs
* Controller 的UnitTest
* 首先在`/main/test`新增一個`web` package,在這個package創造`LocationControllerUnitTest.java`
* `LocationControllerUnitTest.java`新增`@RunWith(SpringRunner.class)`標籤
* 這個會去定義the runner class被使用來測試案例
* 使用SpringRunner因為我們使用Spring to build the application
* `LocationControllerUnitTest.java`新增`@WebMvcTest(LocationController.class)`標籤
* 該標籤的參數放你想要測試的controller
* 新增UnitTest class的屬性`@Autowired private MockMvc mockMvc`
* MockMvc不用去開始一個完整的HTTP server就可以快速地完成測試
* 新增UnitTest class的屬性`@MockBean LocationService locationService`
* 目的在mocking the location service bean,我們將LocationService新增該標籤是因為LocationController依賴於這個Service因此我們需要模擬Service
* 新增測試@Test方法
```java
@Test
public void getAllLocations() throws Exception {
//get代表getRequest
mockMvc.perform(get("/location/"))
//以下三行 可以設定Expectation on HTTP responses received from the controller class
//期望status returned to be Okay
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
//期望回傳的是json
.andExpect(content().json("[]"));
//它僅用於驗證已調用模擬方法的次數
verify(locationService, times(1)).retrieveLocations();
}
@Test
public void getLocation() throws Exception {
mockMvc.perform(get("/location/1"))
.andExpect(status().isOk());
verify(locationService, times(1)).retrieveLocation(1);
}
@Test
public void getLocation() throws Exception {
//使用perform()去模擬Call getRequest
mockMvc.perform(get("/location/1"))
.andExpect(status().isOk());
verify(locationService, times(1)).retrieveLocation(1);
}
```
## 02 Integration Testing for RESTful APIs
# 補充01: MOCK測試
參考: https://waylau.com/mockito-quick-start/
mock最大的功能是幫你把單元測式的耦合分解開,如果你的代碼對另一個class者接口有依賴,他能夠幫你模擬這些依賴並且驗證依賴的行為
> 使用範疇
> * 實例對象有不確定行為和不可預測效果
> * 實例對象難被創建
> * 實例對象難被觸發
> * 實例對象還不存在
> 測試工具
* Mockito, JMock, EasyMock
> Mockito
* 他是java Junit Mock框架