最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

使用 Spring Boot 和 @SpringBootTest 進行測試

2021-11-03 09:51 作者:信碼由韁  | 我要投稿

【注】本文譯自: Testing with Spring Boot and @SpringBootTest - Reflectoring

使用@SpringBootTest 注解,Spring Boot 提供了一種方便的方法來啟動要在測試中使用的應(yīng)用程序上下文。在本教程中,我們將討論何時使用?@SpringBootTest 以及何時更好地使用其他工具進行測試。我們還將研究自定義應(yīng)用程序上下文的不同方法以及如何減少測試運行時間。

? 代碼示例

本文附有?GitHub 上的工作代碼示例。

“使用 Spring Boot 進行測試”系列

本教程是系列的一部分:

  1. 使用 Spring Boot 進行單元測試

  2. 使用 Spring Boot 和?@WebMvcTest 測試 MVC Web Controller

  3. 使用 Spring Boot 和?@DataJpaTest 測試 JPA 查詢

  4. 使用 Spring Boot 和?@SpringBootTest 進行測試

集成測試與單元測試

在開始使用 Spring Boot 進行集成測試之前,讓我們定義集成測試與單元測試的區(qū)別。

單元測試涵蓋單個“單元”,其中一個單元通常是單個類,但也可以是組合測試的一組內(nèi)聚類。

集成測試可以是以下任何一項:

  • 涵蓋多個“單元”的測試。它測試兩個或多個內(nèi)聚類集群之間的交互。

  • 覆蓋多個層的測試。這實際上是第一種情況的特化,例如可能涵蓋業(yè)務(wù)服務(wù)和持久層之間的交互。

  • 涵蓋整個應(yīng)用程序路徑的測試。在這些測試中,我們向應(yīng)用程序發(fā)送請求并檢查它是否正確響應(yīng)并根據(jù)我們的預(yù)期更改了數(shù)據(jù)庫狀態(tài)。

Spring Boot 提供了?@SpringBootTest 注解,我們可以使用它來創(chuàng)建一個應(yīng)用程序上下文,其中包含我們對上述所有測試類型所需的所有對象。但是請注意,過度使用?@SpringBootTest 可能會導(dǎo)致測試套件運行時間非常長。

因此,對于涵蓋多個單元的簡單測試,我們應(yīng)該創(chuàng)建簡單的測試,與單元測試非常相似,在單元測試中,我們手動創(chuàng)建測試所需的對象圖并模擬其余部分。這樣,Spring 不會在每次測試開始時啟動整個應(yīng)用程序上下文。

測試切片

我們可以將我們的 Spring Boot 應(yīng)用程序作為一個整體來測試、一個單元一個單元地測試、也可以一層一層地測試。使用 Spring Boot 的測試切片注解,我們可以分別測試每一層。

在我們詳細研究?@SpringBootTest 注解之前,讓我們探索一下測試切片注解,以檢查?@SpringBootTest 是否真的是您想要的。

@SpringBootTest?注解加載完整的 Spring 應(yīng)用程序上下文。相比之下,測試切片注釋僅加載測試特定層所需的 bean。正因為如此,我們可以避免不必要的模擬和副作用。

@WebMvcTest

我們的 Web 控制器承擔(dān)許多職責(zé),例如偵聽 HTTP 請求、驗證輸入、調(diào)用業(yè)務(wù)邏輯、序列化輸出以及將異常轉(zhuǎn)換為正確的響應(yīng)。我們應(yīng)該編寫測試來驗證所有這些功能。

@WebMvcTest?測試切片注釋將使用剛好足夠的組件和配置來設(shè)置我們的應(yīng)用程序上下文,以測試我們的 Web 控制器層。例如,它將設(shè)置我們的@Controller、@ControllerAdvice、一個?MockMvc bean 和其他一些自動配置。

要閱讀有關(guān) @WebMvcTest 的更多信息并了解我們?nèi)绾悟炞C每個職責(zé),請閱讀我關(guān)于使用 Spring Boot 和 @WebMvcTest 測試 MVC Web 控制器的文章。

@WebFluxTest

@WebFluxTest 用于測試 WebFlux 控制器。@WebFluxTest 的工作方式類似于?@WebMvcTest 注釋,不同之處在于它不是 Web MVC 組件和配置,而是啟動 WebFlux 組件和配置。其中一個 bean 是 WebTestClient,我們可以使用它來測試我們的 WebFlux 端點。

@DataJpaTest

就像?@WebMvcTest 允許我們測試我們的 web 層一樣,@DataJpaTest 用于測試持久層。

它配置我們的實體、存儲庫并設(shè)置嵌入式數(shù)據(jù)庫?,F(xiàn)在,這一切都很好,但是,測試我們的持久層意味著什么? 我們究竟在測試什么? 如果查詢,那么什么樣的查詢?要找出所有這些問題的答案,請閱讀我關(guān)于使用 Spring Boot 和 @DataJpaTest 測試 JPA 查詢的文章。

@DataJdbcTest

Spring Data JDBC 是 Spring Data 系列的另一個成員。 如果我們正在使用這個項目并且想要測試持久層,那么我們可以使用?@DataJdbcTest 注解?。@DataJdbcTest 會自動為我們配置在我們的項目中定義的嵌入式測試數(shù)據(jù)庫和 JDBC 存儲庫。

另一個類似的項目是 Spring JDBC,它為我們提供了?JdbcTemplate 對象來執(zhí)行直接查詢。@JdbcTest?注解自動配置測試我們的 JDBC 查詢所需的?DataSource 對象。

依賴

本文中的代碼示例只需要依賴 Spring Boot 的 test starter 和 JUnit Jupiter:


dependencies {

? ? testCompile('org.springframework.boot:spring-boot-starter-test')

? ? testCompile('org.junit.jupiter:junit-jupiter:5.4.0')

}

使用?@SpringBootTest 創(chuàng)建 ApplicationContext

@SpringBootTest?在默認(rèn)情況下開始在測試類的當(dāng)前包中搜索,然后在包結(jié)構(gòu)中向上搜索,尋找用?@SpringBootConfiguration 注解的類,然后從中讀取配置以創(chuàng)建應(yīng)用程序上下文。這個類通常是我們的主要應(yīng)用程序類,因為?@SpringBootApplication 注解包括?@SpringBootConfiguration 注解。然后,它會創(chuàng)建一個與在生產(chǎn)環(huán)境中啟動的應(yīng)用程序上下文非常相似的應(yīng)用程序上下文。

我們可以通過許多不同的方式自定義此應(yīng)用程序上下文,如下一節(jié)所述。

因為我們有一個完整的應(yīng)用程序上下文,包括 web 控制器、Spring 數(shù)據(jù)存儲庫和數(shù)據(jù)源,@SpringBootTest 對于貫穿應(yīng)用程序所有層的集成測試非常方便:


@ExtendWith(SpringExtension.class)

@SpringBootTest

@AutoConfigureMockMvc

class RegisterUseCaseIntegrationTest {

? @Autowired

? private MockMvc mockMvc;

? @Autowired

? private ObjectMApper objectMApper;

? @Autowired

? private UserRepository userRepository;

? @Test

? void registrationWorksThroughAllLayers() throws Exception {

? ? UserResource user = new UserResource("Zaphod", "zaphod@galaxy.net");

? ? mockMvc.perform(post("/forums/{forumId}/register", 42L)

? ? ? ? ? ? .contentType("Application/json")

? ? ? ? ? ? .param("sendWelcomeMail", "true")

? ? ? ? ? ? .content(objectMApper.writeValueAsString(user)))

? ? ? ? ? ? .andExpect(status().isOk());

? ? UserEntity userEntity = userRepository.findByName("Zaphod");

? ? assertThat(userEntity.getEmail()).isEqualTo("zaphod@galaxy.net");

? }

}

@ExtendWith

本教程中的代碼示例使用 @ExtendWith?注解告訴 JUnit 5 啟用 Spring 支持。從 Spring Boot 2.1 開始,我們不再需要加載 SpringExtension,因為它作為元注釋包含在 Spring Boot 測試注釋中,例如 @DataJpaTest、@WebMvcTest 和 @SpringBootTest。

在這里,我們另外使用?@AutoConfigureMockMvc 將?MockMvc 實例添加到應(yīng)用程序上下文中。

我們使用這個?MockMvc 對象向我們的應(yīng)用程序執(zhí)行?POST 請求并驗證它是否按預(yù)期響應(yīng)。

然后,我們使用應(yīng)用程序上下文中的?UserRepository 來驗證請求是否導(dǎo)致數(shù)據(jù)庫狀態(tài)發(fā)生預(yù)期的變化。

自定義應(yīng)用程序上下文

我們可以有很多種方法來自定義?@SpringBootTest 創(chuàng)建的應(yīng)用程序上下文。讓我們看看我們有哪些選擇。

自定義應(yīng)用上下文時的注意事項

應(yīng)用程序上下文的每個自定義都是使其與在生產(chǎn)設(shè)置中啟動的“真實”應(yīng)用程序上下文不同的另一件事。因此,為了使我們的測試盡可能接近生產(chǎn),我們應(yīng)該只定制讓測試運行真正需要的東西!

添加自動配置

在上面,我們已經(jīng)看到了自動配置的作用:


@SpringBootTest

@AutoConfigureMockMvc

class RegisterUseCaseIntegrationTest {

? ...

}

還有很多其他可用的自動配置,每個都可以將其他 bean 添加到應(yīng)用程序上下文中。以下是文檔中其他一些有用的內(nèi)容:

  • @AutoConfigureWebTestClient:將?WebTestClient 添加到測試應(yīng)用程序上下文。它允許我們測試服務(wù)器端點。

  • @AutoConfigureTestDatabase:這允許我們針對真實數(shù)據(jù)庫而不是嵌入式數(shù)據(jù)庫運行測試。

  • @RestClientTest:當(dāng)我們想要測試我們的?RestTemplate 時它會派上用場。 它自動配置所需的組件以及一個?MockRestServiceServer 對象,該對象幫助我們模擬來自?RestTemplate 調(diào)用的請求的響應(yīng)。

  • @JsonTest:自動配置 JSON 映射器和類,例如?JacksonTester 或 GsonTester。使用這些我們可以驗證我們的 JSON 序列化/反序列化是否正常工作。

設(shè)置自定義配置屬性

通常,在測試中需要將一些配置屬性設(shè)置為與生產(chǎn)設(shè)置中的值不同的值:


@SpringBootTest(properties = "foo=bar")

class SpringBootPropertiesTest {

? @Value("${foo}")

? String foo;

? @Test

? void test(){

? ? assertThat(foo).isEqualTo("bar");

? }

}

如果屬性?foo 存在于默認(rèn)設(shè)置中,它將被此測試的值?bar 覆蓋。

使用 @ActiveProfiles 外部化屬性

如果我們的許多測試需要相同的屬性集,我們可以創(chuàng)建一個配置文件?Application-<profile>.propertie 或?Application-<profile>.yml 并通過激活某個配置文件從該文件加載屬性:


# Application-test.yml foo: bar

@SpringBootTest

@ActiveProfiles("test")

class SpringBootProfileTest {

? @Value("${foo}")

? String foo;

? @Test

? void test(){

? ? assertThat(foo).isEqualTo("bar");

? }

}

使用?@TestPropertySource 設(shè)置自定義屬性

另一種定制整個屬性集的方法是使用?@TestPropertySource 注釋:


# src/test/resources/foo.properties

foo=bar

@SpringBootTest

@TestPropertySource(locations = "/foo.properties")

class SpringBootPropertySourceTest {

? @Value("${foo}")

? String foo;

? @Test

? void test(){

? ? assertThat(foo).isEqualTo("bar");

? }

}

foo.properties?文件中的所有屬性都加載到應(yīng)用程序上下文中。@TestPropertySource 還可以配置更多。

使用 @MockBean 注入模擬

如果我們只想測試應(yīng)用程序的某個部分而不是從傳入請求到數(shù)據(jù)庫的整個路徑,我們可以使用?@MockBean 替換應(yīng)用程序上下文中的某些 bean:


@SpringBootTest

class MockBeanTest {

? @MockBean

? private UserRepository userRepository;

? @Autowired

? private RegisterUseCase registerUseCase;

? @Test

? void testRegister(){

? ? // given

? ? User user = new User("Zaphod", "zaphod@galaxy.net");

? ? boolean sendWelcomeMail = true;

? ? given(userRepository.save(any(UserEntity.class))).willReturn(userEntity(1L));

? ? // when

? ? Long userId = registerUseCase.registerUser(user, sendWelcomeMail);

? ? // then

? ? assertThat(userId).isEqualTo(1L);

? }

?

}

在這種情況下,我們用模擬替換了?UserRepository bean。 使用 Mockito 的?given 方法,我們指定了此模擬的預(yù)期行為,以測試使用此存儲庫的類。

您可以在我關(guān)于模擬的文章中閱讀有關(guān)?@MockBean 注解的更多信息。

使用?@Import 添加 Bean

如果某些 bean 未包含在默認(rèn)應(yīng)用程序上下文中,但我們在測試中需要它們,我們可以使用?@Import 注解導(dǎo)入它們:


package other.namespace;

@Component

public class Foo {

}

@SpringBootTest

@Import(other.namespace.Foo.class)

class SpringBootImportTest {

? @Autowired

? Foo foo;

? @Test

? void test() {

? ? assertThat(foo).isNotNull();

? }

}

默認(rèn)情況下,Spring Boot 應(yīng)用程序包含它在其包和子包中找到的所有組件,因此通常只有在我們想要包含其他包中的 bean 時才需要這樣做。

使用?@TestConfiguration 覆蓋 Bean

使用 @TestConfiguration,我們不僅可以包含測試所需的其他 bean,還可以覆蓋應(yīng)用程序中已經(jīng)定義的 bean。在我們關(guān)于使用 @TestConfiguration 進行測試的文章中閱讀更多相關(guān)信息。

創(chuàng)建自定義 @SpringBootApplication

我們甚至可以創(chuàng)建一個完整的自定義 Spring Boot 應(yīng)用程序來啟動測試。如果這個應(yīng)用程序類與真正的應(yīng)用程序類在同一個包中,但是在測試源而不是生產(chǎn)源中,@SpringBootTest 會在實際應(yīng)用程序類之前找到它,并從這個應(yīng)用程序加載應(yīng)用程序上下文。

或者,我們可以告訴 Spring Boot 使用哪個應(yīng)用程序類來創(chuàng)建應(yīng)用程序上下文:


@SpringBootTest(classes = CustomApplication.class)

class CustomApplicationTest {

}

但是,在執(zhí)行此操作時,我們正在測試可能與生產(chǎn)環(huán)境完全不同的應(yīng)用程序上下文,因此僅當(dāng)無法在測試環(huán)境中啟動生產(chǎn)應(yīng)用程序時,這才應(yīng)該是最后的手段。但是,通常有更好的方法,例如使真實的應(yīng)用程序上下文可配置以排除不會在測試環(huán)境中啟動的 bean。讓我們看一個例子。

假設(shè)我們在應(yīng)用程序類上使用?@EnableScheduling?注解。每次啟動應(yīng)用程序上下文時(即使在測試中),所有?@Scheduled 作業(yè)都將啟動,并且可能與我們的測試沖突。 我們通常不希望作業(yè)在測試中運行,因此我們可以創(chuàng)建第二個沒有?@EnabledScheduling 注釋的應(yīng)用程序類,并在測試中使用它。 但是,更好的解決方案是創(chuàng)建一個可以使用屬性切換的配置類:


@Configuration

@EnableScheduling

@ConditionalOnProperty(

? ? ? ? name = "io.reflectoring.scheduling.enabled",

? ? ? ? havingValue = "true",

? ? ? ? matchIfMissing = true)

public class SchedulingConfiguration {

}

我們已將?@EnableScheduling 注解從我們的應(yīng)用程序類移到這個特殊的配置類。將屬性?io.reflectoring.scheduling.enabled 設(shè)置為?false 將導(dǎo)致此類不會作為應(yīng)用程序上下文的一部分加載:


@SpringBootTest(properties = "io.reflectoring.scheduling.enabled=false")

class SchedulingTest {

? @Autowired(required = false)

? private SchedulingConfiguration schedulingConfiguration;

? @Test

? void test() {

? ? assertThat(schedulingConfiguration).isNull();

? }

}

我們現(xiàn)在已經(jīng)成功地停用了測試中的預(yù)定作業(yè)。屬性?io.reflectoring.scheduling.enabled 可以通過上述任何方式指定。

為什么我的集成測試這么慢?

包含大量?@SpringBootTest 注釋測試的代碼庫可能需要相當(dāng)長的時間才能運行。Spring 的測試支持?足夠智能?,只創(chuàng)建一次應(yīng)用上下文并在后續(xù)測試中重復(fù)使用,但是如果不同的測試需要不同的應(yīng)用上下文,它仍然會為每個測試創(chuàng)建一個單獨的上下文,這需要一些時間來完成每個測試。

上面描述的所有自定義選項都會導(dǎo)致 Spring 創(chuàng)建一個新的應(yīng)用程序上下文。因此,我們可能希望創(chuàng)建一個配置并將其用于所有測試,以便可以重用應(yīng)用程序上下文。

如果您對測試花費在設(shè)置和 Spring 應(yīng)用程序上下文上的時間感興趣,您可能需要查看?JUnit Insights,它可以包含在 Gradle 或 Maven 構(gòu)建中,以生成關(guān)于 JUnit 5 如何花費時間的很好的報告。

結(jié)論

@SpringBootTest?是一種為測試設(shè)置應(yīng)用程序上下文的非常方便的方法,它非常接近我們將在生產(chǎn)中使用的上下文。有很多選項可以自定義此應(yīng)用程序上下文,但應(yīng)謹(jǐn)慎使用它們,因為我們希望我們的測試盡可能接近生產(chǎn)運行。

如果我們想在整個應(yīng)用程序中進行測試,@SpringBootTest 會帶來最大的價值。為了僅測試應(yīng)用程序的某些切片或?qū)樱覀冞€有其他選項可用。

本文中使用的示例代碼可在?githu 上找到。


使用 Spring Boot 和 @SpringBootTest 進行測試的評論 (共 條)

分享到微博請遵守國家法律
梅河口市| 清苑县| 格尔木市| 汝南县| 乐昌市| 南丰县| 建宁县| 三河市| 同仁县| 南部县| 夏津县| 东海县| 克什克腾旗| 牡丹江市| 汽车| 乐业县| 乌审旗| 清镇市| 常州市| 浪卡子县| 江阴市| 宕昌县| 寻乌县| 洛浦县| 郓城县| 宾川县| 溧阳市| 沭阳县| 赣榆县| 襄垣县| 东乡县| 滁州市| 彭州市| 新巴尔虎左旗| 大兴区| 阜阳市| 阳山县| 亚东县| 昌吉市| 冀州市| 苏尼特右旗|