更新時間:2024-02-05 15:11:12作者:佚名
【注】本文譯自:withBootand@-
使用@注解,Boot提供了一種便捷的方式來啟動要在測試中使用的應用程序上下文。在本教程中,我們將討論何時使用@以及何時更好地使用其他工具進行測試。我們還將研究自定義應用程序上下文的不同方式以及怎么減小測試運行時間。
?代碼示例
本文附有上的工作代碼示例。
“使用Boot進行測試”系列
本教程是系列的一部份:
使用Boot進行單元測試
集成測試與單元測試
在開始使用Boot進行集成測試之前,讓我們定義集成測試與單元測試的區(qū)別。
單元測試囊括單個“單元”test是什么意思,其中一個單元一般是單個類,但也可以是組合測試的一組內(nèi)降維。
集成測試可以是以下任何一項:
Boot提供了@注解,我們可以使用它來創(chuàng)建一個應用程序上下文,其中包含我們對上述所有測試類型所需的所有對象。并且請注意,過度使用@可能會造成測試套件運行時間特別長。
因而,對于囊括多個單元的簡單測試,我們應當創(chuàng)建簡單的測試,與單元測試十分相像,在單元測試中,我們自動創(chuàng)建測試所需的對象圖并模擬其余部份。這樣,不會在每次測試開始時啟動整個應用程序上下文。
測試切塊
我們可以將我們的Boot應用程序作為一個整體來測試、一個單元一個單元地測試、也可以一層一層地測試。使用Boot的測試切塊注解,我們可以分別測試每一層。
在我們詳盡研究@注解之前,讓我們探求一下測試切塊注解,以檢測@是否真的是您想要的。
@注解加載完整的應用程序上下文。相比之下,測試切塊注釋僅加載測試特定層所需的bean。正由于這般,我們可以防止何必要的模擬和副作用。
@
我們的Web控制器承當許多職責,比如偵聽HTTP懇求、驗證輸入、調(diào)用業(yè)務邏輯、序列化輸出以及將異常轉(zhuǎn)換為正確的響應。我們應當編撰測試來驗證所有這種功能。
@測試切塊注釋將使用恰好足夠的組件和配置來設置我們的應用程序上下文,以測試我們的Web控制器層。比如,它將設置我們的@、@、一個bean和其他一些手動配置。
@
@用于測試控制器。@的工作方法類似于@注釋,不同之處在于它不是WebMVC組件和配置,而是啟動組件和配置。其中一個bean是,我們可以使用它來測試我們的端點。
@
如同@容許我們測試我們的web層一樣,@用于測試持久層。
它配置我們的實體、存儲庫并設置嵌入式數(shù)據(jù)庫。如今,這一切都挺好,然而,測試我們的持久層意味著哪些?我們到底在測試哪些?假如查詢,這么哪些樣的查詢?要找出所有這種問題的答案,請閱讀我關于使用Boot和@測試JPA查詢的文章。
@
DataJDBC是Data系列的另一個成員。假如我們正在使用這個項目而且想要測試持久層,這么我們可以使用@注解。@會手動為我們配置在我們的項目中定義的嵌入式測試數(shù)據(jù)庫和JDBC儲存庫。
另一個類似的項目是JDBC,它為我們提供了對象來執(zhí)行直接查詢。@注解手動配置測試我們的JDBC查詢所需的對象。
依賴
本文中的代碼示例只須要依賴Boot的test和JUnit:
dependencies {
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.junit.jupiter:junit-jupiter:5.4.0')
}
使用@創(chuàng)建
@在默認情況下開始在測試類的當前包中搜索,之后在包結(jié)構(gòu)中向下搜索,找尋用@ion注解的類,之后從中讀取配置以創(chuàng)建應用程序上下文。這個類一般是我們的主要應用程序類,由于@n注解包括@ion注解。之后,它會創(chuàng)建一個與在生產(chǎn)環(huán)境中啟動的應用程序上下文十分相像的應用程序上下文。
我們可以通過許多不同的形式自定義此應用程序上下文,如下一節(jié)所述。
由于我們有一個完整的應用程序上下文,包括web控制器、數(shù)據(jù)儲存庫和數(shù)據(jù)源,@對于貫串應用程序所有層的集成測試十分便捷:
@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");
}
}
@
本教程中的代碼示例使用@注解告訴JUnit5啟用支持。從Boot2.1開始,我們不再須要加載,由于它作為元注釋包含在Boot測試注釋中,比如@、@和@。
在這兒,我們另外使用@將實例添加到應用程序上下文中。
我們使用這個對象向我們的應用程序執(zhí)行POST懇求并驗證它是否按預期響應。
之后,我們使用應用程序上下文中的來驗證懇求是否造成數(shù)據(jù)庫狀態(tài)發(fā)生預期的變化。
自定義應用程序上下文
我們可以有好多種方式來自定義@創(chuàng)建的應用程序上下文。讓我們瞧瞧我們有什么選擇。
自定義應用上下文時的注意事項
應用程序上下文的每位自定義都是使其與在生產(chǎn)設置中啟動的“真實”應用程序上下文不同的另一件事。為此,為了使我們的測試盡可能接近生產(chǎn),我們應當只訂制讓測試運行真正須要的東西!
添加手動配置
在里面,我們早已聽到了手動配置的作用:
@SpringBootTest
@AutoConfigureMockMvc
class RegisterUseCaseIntegrationTest {
...
}
:還有好多其他可用的手動配置,每位都可以將其他bean添加到應用程序上下文中。以下是文檔中其他一些有用的內(nèi)容:
設置自定義配置屬性
一般,在測試中須要將一些配置屬性設置為與生產(chǎn)設置中的值不同的值:
@SpringBootTest(properties = "foo=bar")
class SpringBootPropertiesTest {
@Value("${foo}")
String foo;
@Test
void test(){
assertThat(foo).isEqualTo("bar");
}
}
假如屬性foo存在于默認設置中,它將被此測試的值bar覆蓋。
使用@外部化屬性
假如我們的許多測試須要相同的屬性集,我們可以創(chuàng)建一個配置文件-
.或-
.yml并通過激活某個配置文件從該文件加載屬性:
# application-test.yml foo: bar
@SpringBootTest
@ActiveProfiles("test")
class SpringBootProfileTest {
@Value("${foo}")
String foo;
@Test
void test(){
assertThat(foo).isEqualTo("bar");
}
}
使用@設置自定義屬性
另一種訂制整個屬性集的方式是使用@注釋:
# 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.文件中的所有屬性都加載到應用程序上下文中。@還可以配置更多。
使用@注入模擬
假如我們只想測試應用程序的某個部份而不是從傳入懇求到數(shù)據(jù)庫的整個路徑,我們可以使用@替換應用程序上下文中的個別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);
}
}
在這些情況下,我們用模擬替換了bean。使用的given方式,我們指定了此模擬的預期行為,以測試使用此儲存庫的類。
您可以在我關于模擬的文章中閱讀有關@注解的更多信息。
使用@添加Bean
假如個別bean未包含在默認應用程序上下文中,但我們在測試中須要它們,我們可以使用@注解導出它們:
package other.namespace;@Componentpublic class Foo {}
@SpringBootTest
@Import(other.namespace.Foo.class)
class SpringBootImportTest {
@Autowired
Foo foo;
@Test
void test() {
assertThat(foo).isNotNull();
}
}
默認情況下,Boot應用程序包含它在其包和子包中找到的所有組件,因而一般只有在我們想要包含其他包中的bean時才須要這樣做。
使用@覆蓋Bean
使用@,我們除了可以包含測試所需的其他bean,還可以覆蓋應用程序中早已定義的bean。在我們關于使用@進行測試的文章中閱讀更多相關信息。
創(chuàng)建自定義@n
我們甚至可以創(chuàng)建一個完整的自定義Boot應用程序來啟動測試。假如這個應用程序類與真正的應用程序類在同一個包中,并且在測試源而不是生產(chǎn)源中,@會在實際應用程序類之前找到它,并從這個應用程序加載應用程序上下文。
或則,我們可以告訴Boot使用那個應用程序類來創(chuàng)建應用程序上下文:
@SpringBootTest(classes = CustomApplication.class)
class CustomApplicationTest {
}
然而,在執(zhí)行此操作時,我們正在測試可能與生產(chǎn)環(huán)境完全不同的應用程序上下文網(wǎng)校頭條,因而僅當未能在測試環(huán)境中啟動生產(chǎn)應用程序時,這才應當是最后的手段。并且test是什么意思,一般有更好的方式,比如使真實的應用程序上下文可配置以排除不會在測試環(huán)境中啟動的bean。讓我們看一個反例。
假定我們在應用程序類上使用@注解。每次啟動應用程序上下文時(雖然在測試中),所有@作業(yè)都將啟動,而且可能與我們的測試沖突。我們一般不希望作業(yè)在測試中運行,因而我們可以創(chuàng)建第二個沒有@注釋的應用程序類,并在測試中使用它。并且,更好的解決方案是創(chuàng)建一個可以使用屬性切換的配置類:
@Configuration
@EnableScheduling
@ConditionalOnProperty(
name = "io.reflectoring.scheduling.enabled",
havingValue = "true",
matchIfMissing = true)
public class SchedulingConfiguration {
}
我們已將@注解從我們的應用程序類移到這個特殊的配置類。將屬性
io...設置為false將造成這種不會作為應用程序上下文的一部份加載:
@SpringBootTest(properties = "io.reflectoring.scheduling.enabled=false")
class SchedulingTest {
@Autowired(required = false)
private SchedulingConfiguration schedulingConfiguration;
@Test
void test() {
assertThat(schedulingConfiguration).isNull();
}
}
我們?nèi)缃褚讶怀晒Φ赝S昧藴y試中的預定作業(yè)。屬性
io...可以通過上述任何方式指定。
為何我的集成測試那么慢?
包含大量@注釋測試的代碼庫可能須要相當長的時間就能運行。的測試支持足夠智能,只創(chuàng)建一次應用上下文并在后續(xù)測試中重復使用,而且假如不同的測試須要不同的應用上下文,它依然會為每位測試創(chuàng)建一個單獨的上下文,這須要一些時間來完成每位測試。
前面描述的所有自定義選項就會造成創(chuàng)建一個新的應用程序上下文。為此,我們可能希望創(chuàng)建一個配置并將其用于所有測試,便于可以重用應用程序上下文。
倘若您對測試耗費在設置和應用程序上下文上的時間感興趣,您可能須要查看JUnit,它可以包含在或Maven建立中,以生成關于JUnit5怎么耗費時間的挺好的報告。
推論
.@是一種為測試設置應用程序上下文的特別便捷的方式,它特別接近我們將在生產(chǎn)中使用的上下文。有很多選項可以自定義此應用程序上下文,但應慎重使用它們,由于我們希望我們的測試盡可能接近生產(chǎn)運行。
假如我們想在整個應用程序中進行測試,@會帶來最大的價值。為了僅測試應用程序的個別切塊或?qū)樱覀冞€有其他選項可用。
本文中使用的示例代碼可在上找到。