在Spring Boot專案撰寫單元測試(Unit Test)時要在測試類別前加上@SpringBootTest
注釋,例如下面是被測試的Controller類別。
DemoController
package com.abc.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class DemoController {
@GetMapping(value = "/hello")
public String hello(HttpServletRequest request) {
String hello = "hello";
System.out.println(hello); // hello
return hello;
}
}
下面則是用來測試DemoController
的測試類,類別名稱前會加上@SpringBootTest
。
DemoControllerTests
package com.abc.demo.controller;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoControllerTests {
@Autowired
private DemoController demoController;
@Test
void hello_ReturnHello() throws Exception {
String actual = demoController.hello();
String expected = "hello";
Assertions.assertEquals(expected, actual);
}
}
@SpringBootTest
的用途為找到@SpringBootApplication
主配置類別來啟動Spring Boot應用程式環境。
@SpringBootTest
預設不會啟動server來執行測試,不過可透過webEnvironment
屬性設定測試時的web環境,設定值如下:
MOCK
(預設):載入使用mock的WebApplicationContext
環境(mock web環境),不會啟動內置server。若classpath沒有Spring Web設定,則載入無web環境的ApplicationContext
。
測試web程式時通常會搭配@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
做mock測試。RANDOM_PORT
:載入真實的WebServerApplicationContext
,會啟動監聽隨機port的內置server。DEFINED_PORT
:載入真實的WebServerApplicationContext
,會啟動監聽指定port的內置server。port定義在application.properties
,預設8080
NONE
:載入無web環境的ApplicationContext
。
各設定測試DemoController
範例如下。
範例環境:
- Java 1.8
- Spring Boot 2.2.1.RELEASE
- JUnit 5
範例的application.properties
設定。
application.properties
#context path
server.servlet.context-path=/demo
#port
server.port=8080
SpringBootTest.WebEnvironment.MOCK
使用MOCK
,則執行測試時為mock Web環境,server不啟動,因此搭配@AutoConfigureMockMvc
並注入MockMvc
來做mock端點(endpoints)測試。
DemoControllerTests
package com.abc.demo.controller;
import org.junit.jupiter.api.Test;
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.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class DemoControllerTests {
@Autowired
private MockMvc mockMvc;
@Test
void hello_ReturnHello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello")); // pass
}
}
SpringBootTest.WebEnvironment.RANDOM_PORT
使用RANDOM_PORT
,則執行測試時為真實的web環境,server會啟動,port號為隨機。可使用@LocalServerPort
注入使用的隨機port號。利用TestRestTemplate
對端點發送請求來進行測試。
DemoControllerTests
package com.abc.demo.controller;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoControllerTests {
@Autowired
private TestRestTemplate testRestTemplate;
@LocalServerPort
private int port; // random port
@Test
void hello_ReturnHello() throws Exception {
String url = "http://localhost:" + port + "/demo/hello"; // http://localhost:62214/demo/hello
System.out.println(url);
Assertions.assertTrue(
testRestTemplate.getForObject(url, String.class)
.contains("hello")); // pass
}
}
SpringBootTest.WebEnvironment.DEFINED_PORT
使用DEFINED_PORT
,則執行測試時為真實的web環境,server會啟動,port號為application.properties
中設定的port號。可使用@LocalServerPort
注入port號。利用TestRestTemplate
對端點發送請求來進行測試。
DemoControllerTests
package com.abc.demo.controller;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class DemoControllerTests {
@Autowired
private TestRestTemplate testRestTemplate;
@LocalServerPort
private int port;
@Test
void hello_ReturnHello() throws Exception {
String url = "http://localhost:" + port + "/demo/hello"; // http://localhost:8080/demo/hello
System.out.println(url);
Assertions.assertTrue(
testRestTemplate.getForObject(url, String.class)
.contains("hello")); // pass
}
}
SpringBootTest.WebEnvironment.NONE
使用NONE
,則執行測試時無web環境,不啟動server,所以無法做真正或mock的端點測試。
DemoControllerTests
package com.abc.demo.controller;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class DemoControllerTests {
@Autowired
private DemoController demoController;
@Test
void hello_ReturnHello() throws Exception {
String actual = demoController.hello();
String expected = "hello";
Assertions.assertEquals(expected, actual); // pass
}
}
參考:
最近在試spring boot 的整合測試,啟動test時遇到一堆警告訊息如下:
回覆刪除Did not detect default resource location for test class
以及想實際讓spring在單元測試時啟動可以模擬用client連線進行整合測試,網路找了一堆都是片段治標不治本解法,而且我試了也不行,搜尋到最後看到你這篇時我心想穩了(因為認識你,哈哈),應該是有用的,之後詳細看了一下文章裡的範例很完整,最後測試也可以用,非常感謝你
哈哈,謝謝JAVA吉他手的抬舉,能幫到您我很開心。
回覆刪除