springboot junit Unit-Testing(via spring-boot-starter-test)

Moshow鄭鍇發表於2020-04-07

SpringBoot Testing

Spring Boot預設提供了一系列實用工具Annotation註釋來幫助您測試應用,可以用來做單元測試Unit Testing

測試支援由兩個模組提供:spring-boot-test包含核心項,spring-boot-test-autoconfigure支援測試的自動配置。

大多數開發人員使用spring-boot-starter-test的 Starter 模組,它匯入Spring Boot測試模組以及JUnit,AssertJ,Hamcrest和許多其他有用的庫。

Spring Boot Testing 官方文件

在這裡插入圖片描述
Maven Dependency

直接新增這個依賴即可。

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

SpringBoot Application

沒什麼特別的,跟往常一樣,無需額外配置。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JunitApplication {
	public static void main(String[] args) {
		SpringApplication.run(JunitApplication.class,args);
	}
}

TestController

新建一個方法,返回一些JSON資料用於測試即可。

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;


@RestController
public class TestController {

    @GetMapping("/user")
    public ResponseEntity user(){
        Map<String,Object> user=new HashMap<>(2);
        user.put("id","6666");
        user.put("createtime",System.currentTimeMillis());
        user.put("name","MOSHOW.K.ZHENG");
        return ResponseEntity.ok(user);
    }
}

UnitTest.java

Spring Boot應用程式是一個Spring ApplicationContext,因此除了通常使用的Spring上下文之外,還沒有什麼特別的東西可以用來測試它。

Spring Boot 提供了強大的@SpringBootTest註解,可以直接通過你的SpingApplication注入ApplicationContext,並提供很多可選引數,請看下面的引數解析。

@RunWith(SpringRunner.class) 部分:

  • 如果您使用的是JUnit 4,請不要忘記將@RunWith(SpringRunner.class)新增到測試中,否則將忽略註釋。
  • 如果您正在使用JUnit 5,則無需將等效的@ExtendWith(SpringExtension.class)新增到@SpringBootTest旁邊。

@SpringBootTest - WebEnvironment部分:

  • WebEnvironment.MOCK(預設):載入Web的ApplicationContext並提供模擬Web環境。 使用此批註時,不會啟動嵌入式伺服器例如Tomcat。 如果類路徑上沒有Web環境,則此模式將透明地回退到建立常規非Web ApplicationContext。 它可以與@AutoConfigureMockMvc或@AutoConfigureWebTestClient結合使用,以進行基於模擬的Web應用程式測試。
  • WebEnvironment.RANDOM_PORT建議!):載入WebServerApplicationContext 並提供一個真實的Web環境,內嵌的伺服器例如Tomcat將會使用隨機埠進行監聽,可以用 @LocalServerPort 獲取當前隨機的埠號。
  • WebEnvironment.DEFINED_PORT:載入WebServerApplicationContext 並提供一個真實的Web環境,內嵌的伺服器例如Tomcat將會從application.yml等配置檔案載入,萬一沒有則預設8080。
  • WebEnvironment.NONE:通過SpringApplication載入ApplicationContext給你,但不提供任何Web環境以及啟用任何伺服器。

那麼,怎麼呼叫Contoller中方法的URL呢?

  • TestRestTemplate,for WebMVC testing。TestRestTemplate是Spring的RestTemplate的一種便利替代品,可用於單元測試/整合測試。在任何一種情況下,模板都以一種測試友好的方式執行,不會在伺服器端錯誤上丟擲異常。建議(預設,但不是強制性的)使用Apache HTTP Client(版本4.3.2或更高版本)。可以直接在整合測試中例項化,忽然cookie(因此模板是無狀態的),不遵循重定向(Redirects are not followed,因此您可以斷言響應位置)。
  • WebTestClient,for WebFlux testing。Spring Framework 5.0提供了一個新的WebTestClient,適用於WebFlux整合測試以及WebFlux和MVC端到端測試。與TestRestTemplate不同,它為斷言提供了流暢的API。
import com.alibaba.fastjson.JSON;
import com.softdev.system.demo.JunitApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import java.net.URL;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(classes={JunitApplication.class},webEnvironment = WebEnvironment.RANDOM_PORT)
@Slf4j
public class UnitTest {

    @LocalServerPort
    private int port;

    private URL base;

    private TestRestTemplate template = new TestRestTemplate();

    @Before
    public void setUp() throws Exception {
        String url = String.format("http://127.0.0.1:%d/", port);
        System.out.println(String.format("port is : [%d]", port));
        this.base = new URL(url);
    }
    @Test
    public void userTest() {
        ResponseEntity<String> response = template.getForEntity(this.base.toString() + "/junit/user",String.class,"");
        log.info("ResponseEntity:"+ JSON.toJSONString(response));
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("namex");
        log.info("pass unit-testing");
    }
    @Test
    public void userErrorTest() {
        ResponseEntity<String> response = template.getForEntity(this.base.toString() + "/junit/user",String.class,"");
        log.info("ResponseEntity:"+ JSON.toJSONString(response));
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("namexxx");
        log.info("pass unit-testing");
    }
}

官方還提供一種MockMvc的模擬方式,可以讓Controller從模擬的Service中獲取資料(也可以模擬其他層,例如Repository)

import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(UserVehicleController.class)
public class MyControllerTests {

	@Autowired
	private MockMvc mvc;

	@MockBean
	private UserVehicleService userVehicleService;

	@Test
	public void testExample() throws Exception {
		given(this.userVehicleService.getVehicleDetails("sboot"))
				.willReturn(new VehicleDetails("Honda", "Civic"));
		this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
				.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
	}

}

相關文章