本章從第7章開始:
7. Task Execution and Scheduling
在上下文中沒有Executor bean的情況下,Spring Boot會自動配置一個ThreadPoolTaskExecutor,它具有合理的默認值,可以自動關聯(lián)到異步任務執(zhí)行(@EnableAsync)和Spring MVC異步請求處理。
如果你在上下文中定義了自定義Executor,常規(guī)任務執(zhí)行(即@EnableAsync)將透明地使用它,但不會配置Spring MVC支持,因為它需要AsyncTaskExecutor實現(xiàn)(名為applicationTaskExecutor)。根據(jù)您的目標安排,您可以將Executor更改為ThreadPoolTaskExecutor或定義ThreadPoolTaskExecutor和AsyncConfigurer封裝您的自定義Executor。
自動配置的TaskExecutorBuilder允許您輕松地創(chuàng)建實例來重現(xiàn)默認情況下自動配置所做的事情。
線程池使用8個核心線程,這些線程可以根據(jù)負載增長和收縮。這些默認設置可以使用spring.task.execution命名空間進行微調,如下例所示:
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s
測試:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class TestService {
@Async("taskExecutor")
public void test() {
// do something
}
}
在這個測試代碼中,@Async注解表示這個方法是異步執(zhí)行的,"taskExecutor"表示使用的線程池的名稱。在使用時,只需要注入TestService并調用test()方法即可。
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
@ConfigurationPropertiesScan(basePackages = "com.example.demo.demos")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
application.run(args);
}
}
開啟一下Spring的異步特性的。
這會將線程池更改為使用有界隊列,以便當隊列滿時(100個任務),線程池增加到最多16個線程。當線程空閑10秒(而不是默認的60秒)時回收它們,池的收縮會更加激進。
如果需要與計劃任務執(zhí)行相關聯(lián)(例如使用@ enablesscheduling), ThreadPoolTaskScheduler也可以自動配置。線程池默認使用一個線程,可以使用spring.task.scheduling命名空間對其設置進行微調,如下例所示:
spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
如果需要創(chuàng)建自定義執(zhí)行器或調度器,那么TaskExecutorBuilder bean和TaskSchedulerBuilder bean都可以在上下文中使用。
8. Testing
Spring Boot提供了許多實用工具和注釋,可以在測試應用程序時提供幫助。測試支持由兩個模塊提供:spring-boot-test包含核心項,spring-boot-test-autoconfigure支持測試的自動配置。
大多數(shù)開發(fā)人員使用Spring - Boot - Starter -test“Starter”,它導入Spring - Boot測試模塊以及JUnit Jupiter、AssertJ、Hamcrest和許多其他有用的庫。
如果您有使用JUnit 4的測試,那么可以使用JUnit 5的老式引擎來運行它們。要使用復古引擎,需要在junit-vintage-engine上添加一個依賴項,如下例所示:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
8.1. Test Scope Dependencies
spring-boot-starter-test“Starter”(在測試范圍內(nèi))包含以下提供的庫:
-
JUnit 5:單元測試Java應用程序的事實標準
-
Spring Test?& Spring Boot Test: 對Spring Boot應用程序的實用程序和集成測試支持。
-
AssertJ: 一個流暢的斷言庫
-
Hamcrest: 匹配器對象庫(也稱為約束或謂詞)。
-
Mockito: 一個Java模擬框架。
-
JSONassert: 一個JSON斷言庫。
-
JsonPath: 用于JSON的XPath。
我們通常發(fā)現(xiàn)這些公共庫在編寫測試時很有用。如果這些庫不能滿足您的需求,您可以添加自己的額外測試依賴項。
8.2. Testing Spring Applications
依賴注入的一個主要優(yōu)點是,它可以使代碼更容易進行單元測試。您可以通過使用new操作符實例化對象,甚至不需要使用Spring。您還可以使用模擬對象來代替真正的依賴項。
通常,您需要超越單元測試并開始集成測試(使用Spring ApplicationContext)。能夠在不需要部署應用程序或連接到其他基礎設施的情況下執(zhí)行集成測試是很有用的。
Spring框架包括一個專門用于這種集成測試的測試模塊。你可以直接聲明一個依賴于org.springframework:spring-test或使用spring-boot-starter-test“Starter”來傳遞地拉入它。
8.3. Testing Spring Boot Applications
Spring Boot應用程序是一個Spring ApplicationContext,所以除了通常使用普通Spring上下文所做的測試之外,不需要做任何特別的事情來測試它。
默認情況下,只有在使用SpringApplication創(chuàng)建時,Spring Boot的外部屬性、日志記錄和其他特性才會安裝在上下文中。
SpringBoot提供了一個@SpringBootTest注釋,當您需要SpringBoot特性時,它可以作為標準Spring -test @ContextConfiguration注釋的替代。注釋的工作原理是通過SpringApplication創(chuàng)建測試中使用的ApplicationContext。除了@SpringBootTest之外,還提供了許多其他注釋,用于測試應用程序的更具體的片段。
如果您使用的是JUnit 4,不要忘記在測試中添加@RunWith(sprinrunner .class),否則注釋將被忽略。如果你使用的是JUnit 5,就不需要添加@ extendwith (SpringExtension.class)作為@ springboottest和其他@…Test注釋已經(jīng)用它進行了注釋
默認情況下,@SpringBootTest不會啟動服務器。你可以使用@SpringBootTest的webEnvironment屬性來進一步優(yōu)化測試的運行方式:
MOCK(默認):加載web ApplicationContext并提供模擬web環(huán)境。使用此注釋時,嵌入式服務器不會啟動。如果在你的類路徑上沒有一個web環(huán)境可用,這種模式就會透明地退回到創(chuàng)建一個常規(guī)的非web ApplicationContext。它可以與@AutoConfigureMockMvc或@AutoConfigureWebTestClient一起使用,用于基于模擬的web應用程序測試。
RANDOM_PORT:加載WebServerApplicationContext并提供一個真實的web環(huán)境。嵌入式服務器啟動并在隨機端口上偵聽。
DEFINED_PORT:加載WebServerApplicationContext并提供一個真實的web環(huán)境。嵌入式服務器在一個定義的端口(來自application.properties)上啟動并偵聽,或者在默認端口8080上偵聽。
NONE:通過使用SpringApplication加載ApplicationContext,但不提供任何web環(huán)境(mock或其他)。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class DemoApplicationTests {
或者:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTests {
如果您的測試是@Transactional的,默認情況下,它會在每個測試方法結束時回滾事務。然而,使用RANDOM_PORT或DEFINED_PORT的這種安排隱式地提供了一個真正的servlet環(huán)境,HTTP客戶端和服務器在單獨的線程中運行,因此在單獨的事務中運行。在這種情況下,服務器上發(fā)起的任何事務都不會回滾。
@SpringBootTest with webEnvironment = webEnvironment。如果應用程序為管理服務器使用不同的端口,RANDOM_PORT還將在一個單獨的隨機端口上啟動管理服務器。
8.3.1. Detecting Web Application Type
如果Spring MVC可用,則配置一個常規(guī)的基于MVC的應用程序上下文。如果你只有Spring WebFlux,我們會檢測并配置一個基于WebFlux的應用上下文。
如果兩者都存在,則優(yōu)先考慮Spring MVC。如果你想在這種情況下測試一個響應式web應用程序,你必須設置spring.main.web-application-type屬性:
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
// ...
}
8.3.2. Detecting Test Configuration
如果您熟悉Spring測試框架,您可能習慣于使用@ContextConfiguration(classes=…)來指定加載哪個Spring @Configuration?;蛘?,您可能經(jīng)常在測試中使用嵌套的@Configuration類。
在測試Spring Boot應用程序時,通常不需要這樣做。Spring Boot的@*Test注釋在您沒有顯式定義主配置時自動搜索主配置。
搜索算法從包含測試的包開始查找,直到找到帶有@SpringBootApplication或@SpringBootConfiguration注釋的類。只要以一種合理的方式組織代碼,通常就能找到主配置。
如果您使用測試注釋來測試應用程序中更具體的部分,那么您應該避免在主方法的應用程序類中添加特定于特定區(qū)域的配置設置。
@SpringBootApplication的底層組件掃描配置定義了用于確保切片按預期工作的排除過濾器。如果你在@ springbootapplication注釋的類上使用顯式的@ComponentScan指令,請注意這些過濾器將被禁用。如果您正在使用切片,則應該重新定義它們。
如果你想定制主配置,你可以使用嵌套的@TestConfiguration類。與嵌套的@Configuration類不同,嵌套的@TestConfiguration類是用來代替應用程序的主要配置的,而嵌套的@TestConfiguration類是用來代替應用程序的主要配置的。
Spring的測試框架在測試之間緩存應用程序上下文。因此,只要您的測試共享相同的配置(無論如何發(fā)現(xiàn)它),加載上下文的潛在耗時過程只會發(fā)生一次。
8.3.3. Excluding Test Configuration
如果您的應用程序使用組件掃描(例如,如果您使用@SpringBootApplication或@ComponentScan),您可能會發(fā)現(xiàn)僅為特定測試創(chuàng)建的頂級配置類意外地在各處被拾取。
正如我們前面看到的,@TestConfiguration可以在測試的內(nèi)部類上使用,以定制主配置。當放置在頂級類上時,@TestConfiguration表示src/test/java中的類不應該被掃描。然后你可以在需要的地方顯式導入這個類,如下面的例子所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
// ...
}
}
如果你直接使用@ComponentScan(也就是說,不是通過@SpringBootApplication),你需要注冊TypeExcludeFilter。
8.3.4. Using Application Arguments
如果應用程序需要參數(shù),可以讓@SpringBootTest使用args屬性注入?yún)?shù)。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
8.3.5. Testing With a Mock Environment
默認情況下,@SpringBootTest不會啟動服務器,而是為測試web端點設置一個模擬環(huán)境。
使用Spring MVC,我們可以使用MockMvc或WebTestClient查詢我們的web端點,如下面的例子所示:
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.reactive.server.WebTestClient;
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;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
@AutoConfigureMockMvc它可以自動配置MockMvc并將其注入到測試類中。在使用Spring Boot進行單元測試時,我們可以使用它來模擬HTTP請求和響應,以便測試我們的控制器是否按預期工作。
如果你只想關注web層,而不想啟動一個完整的ApplicationContext,可以考慮使用@WebMvcTest。
使用Spring WebFlux端點,你可以使用WebTestClient,如下例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
在模擬環(huán)境中進行測試通常比在完整的servlet容器中運行要快。然而,由于mock發(fā)生在Spring MVC層,依賴于低級servlet容器行為的代碼不能直接用MockMvc進行測試。
例如,Spring Boot的錯誤處理基于servlet容器提供的“錯誤頁”支持。這意味著,雖然您可以按照預期測試MVC層拋出和處理異常,但您不能直接測試是否呈現(xiàn)了特定的自定義錯誤頁面。如果您需要測試這些較低級別的關注點,您可以啟動一個完全運行的服務器,如下一節(jié)所述。
8.3.6. Testing With a Running Server
如果您需要啟動一個完整運行的服務器,我們建議您使用隨機端口。如果您使用@SpringBootTest(webEnvironment= webEnvironment . random_port),那么每次測試運行時都會隨機選擇一個可用的端口。
可以使用@LocalServerPort注釋將實際使用的端口注入到測試中。為方便起見,需要對已啟動服務器進行REST調用的測試可以另外@Autowire一個WebTestClient,它解析到正在運行的服務器的相對鏈接,并附帶一個專用的API來驗證響應,如下例所示:
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.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
WebTestClient既可以用于活動服務器,也可以用于模擬環(huán)境。
這個設置需要類路徑上的spring-webflux。如果你不能或不打算添加webflux, Spring Boot也提供了一個TestRestTemplate工具:文章來源:http://www.zghlxwxcb.cn/news/detail-714026.html
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.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
記得點關注文章來源地址http://www.zghlxwxcb.cn/news/detail-714026.html
到了這里,關于SpringCore完整學習教程6,入門級別的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!