在前面的文章中,我們已經測試了 Service
、Repository
和 Controller
的部分
今天要來探討,如何在 Spring Boot 裡面實作端對端測試
SpringBootTest
是 Spring Boot 提供的一個強大的測試工具
它讓我們能在測試環境中啟動完整的 Spring 應用程式環境
這意味著我們可以測試整個應用程式的行為,從控制器到資料庫,就像在真實環境中運行一樣
整合測試
和端對端測試
會消耗更多的系統資源首先,在 src/test
下建立 resources
目錄,並在其中建立 application-test.properties
檔案
# 使用記憶體資料庫
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# 指定 JPA 使用的資料庫語言,這告訴 Hibernate 使用 H2 特定的 SQL 語法
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# 在應用程式啟動時自動建立資料庫結構(表格等),並在應用程式關閉時自動刪除
spring.jpa.hibernate.ddl-auto=create-drop
在 test
的 package 下面,建立 TodoEndToEndTest
測試類別
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.properties")
public class TodoEndToEndTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private TodoRepository todoRepository;
@Autowired
private ObjectMapper objectMapper;
@BeforeEach
void setUp() {
todoRepository.deleteAll();
}
@Test
void createTodo() {
// 呼叫 API
// 這裡示範了怎麼使用完整 url (port) 來呼叫
// 在使用 TestRestTemplate 就可以不用指定主機和 port 號,這裡只是示範
String url = "http://localhost:" + port + "/api/todos";
Todo newTodo = new Todo(null, "測試待辦事項", false);
var response = restTemplate.postForEntity(url, newTodo, MyApiResponse.class);
// 驗證回應資料
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertTrue(response.getBody().isSuccess());
Todo responseTodo = objectMapper.convertValue(response.getBody().getData(), new TypeReference<>() {});
assertEquals("測試待辦事項", responseTodo.getTitle());
assertFalse(responseTodo.isCompleted());
// 驗證資料庫資料
List<Todo> todos = todoRepository.findAll();
assertEquals(1, todos.size());
assertEquals("測試待辦事項", todos.get(0).getTitle());
assertFalse(todos.get(0).isCompleted());
}
@Test
void getAllTodos() {
// 新增測試資料
Todo todo1 = new Todo(null, "測試待辦事項", false);
Todo todo2 = new Todo(null, "測試待辦事項2", true);
todoRepository.saveAll(Arrays.asList(todo1, todo2));
// 呼叫 API
var response = restTemplate.getForEntity("/api/todos", MyApiResponse.class);
// 驗證回應資料
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertTrue(response.getBody().isSuccess());
List<Todo> todos = objectMapper.convertValue(response.getBody().getData(), new TypeReference<>() {});
assertEquals(2, todos.size());
assertEquals("測試待辦事項", todos.get(0).getTitle());
assertFalse(todos.get(0).isCompleted());
assertEquals("測試待辦事項2", todos.get(1).getTitle());
assertTrue(todos.get(1).isCompleted());
}
@Test
void getTodo() {
// 新增測試資料
Todo todo = todoRepository.save(new Todo(null, "測試待辦事項", false));
// 呼叫 API
var response = restTemplate.getForEntity("/api/todos/" + todo.getId(), MyApiResponse.class);
// 驗證回應資料
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertTrue(response.getBody().isSuccess());
Todo responseTodo = objectMapper.convertValue(response.getBody().getData(), new TypeReference<>() {});
assertEquals("測試待辦事項", responseTodo.getTitle());
assertFalse(responseTodo.isCompleted());
}
@Test
void updateTodo() {
// 新增測試資料
Todo todo = todoRepository.save(new Todo(null, "原始待辦事項", false));
// 呼叫 API
Todo updatedTodo = new Todo(todo.getId(), "更新後的待辦事項", true);
restTemplate.put("/api/todos/" + todo.getId(), updatedTodo);
// 驗證資料庫資料
Todo actualTodo = todoRepository.findById(todo.getId()).get();
assertEquals("更新後的待辦事項", actualTodo.getTitle());
assertTrue(actualTodo.isCompleted());
}
@Test
void testDeleteTodo() {
// 新增測試資料
Todo savedTodo = todoRepository.save(new Todo(null, "要刪除的待辦事項", false));
// 呼叫 API
restTemplate.delete("/api/todos/" + savedTodo.getId());
// 驗證資料庫資料
List<Todo> todos = todoRepository.findAll();
assertEquals(0, todos.size());
}
}
@SpringBootTest
啟動完整的應用程式
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
時,Spring Boot 會啟動一個測試用的應用程式,並使用一個隨機
的可用 port 號
@TestPropertySource
指定使用測試專用的屬性文件
@LocalServerPort
可以獲取測試用應用程式隨機分配的 port 號
TestRestTemplate
,它是 Spring Boot 提供的用於進行 HTTP 請求的測試工具
TestRestTemplate
會自動配置為使用正確的主機和 port 號,所以在大多數情況下,都不需要顯式的使用 @LocalServerPort
objectMapper
來轉換成 Todo 相關的物件SpringBootTest
為我們提供了一個強大的工具,用於進行整合測試
和 端對端測試
透過這種方式,我們可以確保整個應用程式的各個元件能夠正確地協同運作
雖然這種測試相比單元測試更耗時且消耗更多資源,但它能夠捕捉到單元測試可能遺漏的問題,特別是在組件集成方面的問題
在實際開發中,我們應該結合單元測試和端對端測試,以確保應用程式的品質和可靠性
同步刊登於 Blog 「Spring Boot API 開發:從 0 到 1」Day 29 SpringBootTest 測試
我的粉絲專頁
圖片來源:AI 產生