工程升級(jí)SpringBoot之后,突然發(fā)現(xiàn)之前寫的幾個(gè)簡(jiǎn)單的單元測(cè)試類無(wú)法正常執(zhí)行了,因?yàn)镾pringBoot工程的配置方式與之前還是有比較大的差異。
而且之前直接使用Junit來(lái)寫單元測(cè)試,這一次打算直接升級(jí)到SpringBoot的Test方式。
1、引入依賴包
之前是直接引用junit依賴包,需要更改為spring-boot-starter-test(里面包含了對(duì)junit的依賴)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2、對(duì)于需要?jiǎng)?chuàng)建Unit Test的類/方法,直接在IDEA里右鍵->go to -> Test
IDEA會(huì)自動(dòng)創(chuàng)建對(duì)應(yīng)的Test類,然后編寫對(duì)應(yīng)的測(cè)試代碼即可。
需要注意的是,SpringBoot有默認(rèn)的約定,測(cè)試類需要與被測(cè)試類在相同的package下(目錄結(jié)構(gòu)一個(gè)是main,一個(gè)是test,后面的目錄結(jié)構(gòu)都是一樣的)
在創(chuàng)建好的類上需要加上Test相關(guān)的注解即可:
@SpringBootTest
@RunWith(SpringRunner.class)
方法上加上@Test注解(與Junit一樣的)
建議創(chuàng)建一個(gè)基類,然后將注解直接放在基類上,這樣其他的測(cè)試類可以直接extends該基類即可。
3、由于工程配置文件里面有敏感信息,故配置文件都不在工程默認(rèn)目錄下,而是通過(guò)啟動(dòng)參數(shù)指定的配置文件
此時(shí),對(duì)于Test類,就需要通過(guò)參數(shù)進(jìn)行指定配置文件,來(lái)覆蓋默認(rèn)的配置。
@SpringBootTest
@RunWith(SpringRunner.class)
@TestPropertySource(
locations = { "file:XXXXX\\application-dev.properties", "file:XXXXX\\bootstrap-dev.properties" }, properties = "spring.cloud.nacos.discovery.password=YYYYYYY")
需要注意的是,SpringBoot的TestPropertySource是不支持yml文件格式的,目前只能通過(guò)properties文件來(lái)進(jìn)行配置。(個(gè)別參數(shù)可以直接通過(guò)properties參數(shù)進(jìn)行指定)
https://github.com/spring-projects/spring-boot/issues/10772https://github.com/spring-projects/spring-boot/issues/10772
試了很多網(wǎng)上的方法都沒有成功(Spring @PropertySource using YAML - Stack Overflow),最后看到SpringBootTest注解上支持args參數(shù),可以通過(guò)args指定啟動(dòng)參數(shù),這樣就可以通過(guò)spring.config.location來(lái)指定yml配置文件了。
需要注意,如果args有多個(gè)參數(shù)的時(shí)候,解析好像有BUG,無(wú)法正常處理,比如下面的配置,如果將nacos的password也放在args里面,會(huì)導(dǎo)致password錯(cuò)誤。
@SpringBootTest( args = "--spring.cloud.nacos.discovery.password=YYYYYY?--spring.config.location=classpath:/,XXXXXX\\application-dev.yml,XXXXXX\\bootstrap-dev.yml")
需要改成如下形式才可以:
@SpringBootTest(properties = "spring.cloud.nacos.discovery.password=YYYYYYY",
args = "--spring.config.location=classpath:/,XXXXXX\\application-dev.yml,XXXXXX\\bootstrap-dev.yml")
4、前面提到SpringBoot有默認(rèn)的約定,自動(dòng)生成的測(cè)試類會(huì)在相同的package下,但是如果我需要自定義一些測(cè)試類,與main下面的類或者方法沒有關(guān)聯(lián)。此時(shí)自定義的類放在我們自定義的一個(gè)package里面。
如果此時(shí)直接繼承上面的基類,則會(huì)出現(xiàn):Unable to find a @SpringBootConfiguration,異常。
其實(shí),主要是SpringBootTest在啟動(dòng)時(shí),默認(rèn)進(jìn)行了特定目錄的掃描,找到啟動(dòng)類:SpringBootApplication注解標(biāo)注的類。
而此時(shí)自定義的類在test目錄下的package里面,與main不再同一個(gè)目錄,故無(wú)法掃描到啟動(dòng)類。
此時(shí),只需要增加一個(gè)參數(shù)指定一下啟動(dòng)類即可。(springboot單元測(cè)試Unable to find a @SpringBootConfiguration的兩種解決方法以及原理 - 簡(jiǎn)書springboot單元測(cè)試大部分情況很簡(jiǎn)單,只用增加2個(gè)注解就行: 注意是大部分情況,因?yàn)閟pringboot約定大于配置,如果你不按它的約定,就會(huì)出現(xiàn)下面的錯(cuò)誤Unabl...https://www.jianshu.com/p/1b80e5c4bb02)
@SpringBootTest(classes = TaApplication.class)
5、SpringBootTest會(huì)加載整個(gè)ApplicationContext,所以可以在Test類里直接使用@Autowired來(lái)注入其他的bean。
但是會(huì)有兩種特殊情況需要處理,一種是只需要簡(jiǎn)單測(cè)試某一個(gè)類,不希望加載所有的context(會(huì)比較耗時(shí));另一種是在test目錄下定義了一個(gè)bean對(duì)象,但是在默認(rèn)的context下不會(huì)掃描test下的bean,導(dǎo)致autowired時(shí)候無(wú)法找到該bean。
此時(shí)也有兩種方式來(lái)處理:
1>?定義一個(gè)configuration類,然后指定需要掃描的類或者package,然后在SpringBootTest上指定需要啟動(dòng)的類為該configuration類即可。
@Configuration
@ComponentScan("com.spring.temp")
public class TestConfig {
}
@SpringBootTest(classes = TestConfig.class)
public class TimeLogTest extends BaseTest {……}
說(shuō)明,因?yàn)橛袝r(shí)候需要測(cè)試的類依賴比較多,需要全部加載context,但是又依賴一個(gè)test目錄下的bean,此時(shí)也可以通過(guò)此方式來(lái)解決。
將configuration類的掃描范圍擴(kuò)大
@Configuration
@ComponentScan("com.spring.*")
public class TestConfig {
}
然后,在測(cè)試類上指定TestConfig類為啟動(dòng)類,通過(guò)properties和args指定相應(yīng)參數(shù)即可。
@SpringBootTest(classes = TestConfig.class,
properties = "spring.cloud.nacos.discovery.password=YYYYYY",
args = "--spring.config.location=classpath:/,XXXXXX\\application-dev.yml,XXXXXX\\bootstrap-dev.yml")
public class TimeLogTest extends BaseTest {……}
2>?不使用SpringBootTest來(lái)加載,通過(guò)TestConfiguration注解來(lái)配置(其實(shí)與SpringBootTest指定configuration類是一樣的原理)
@RunWith(SpringRunner.class)
public class TimeLogTest2 {
@TestConfiguration
static class TestConfig {
@Bean
public MethodTimeLogService methodTimeLogService() {
return new MethodTimeLogService();
}
}
@Autowired
private MethodTimeLogService methodTimeLogService;
@Test
public void test() {
methodTimeLogService.logTest();
System.out.println(methodTimeLogService.logTest2());
System.out.println(methodTimeLogService.logTest3("333333", new String[] { "value2", "value22" }));
}
}
Testing in Spring Boot | BaeldungLearn about how the Spring Boot supports testing, to write unit tests efficiently.https://www.baeldung.com/spring-boot-testing
Configuring base package for component scan in Spring boot test - Stack Overflowhttps://stackoverflow.com/questions/48747421/configuring-base-package-for-component-scan-in-spring-boot-test
6、關(guān)于日志xml文件
為了方便對(duì)不同環(huán)境進(jìn)行不同的日志配置(比如日志級(jí)別,日志文件路徑等),在log4j2的XML里使用了變量引用。(https://logging.apache.org/log4j/2.x/log4j-spring-boot/index.htmlhttps://logging.apache.org/log4j/2.x/log4j-spring-boot/index.html)
在XML配置文件里面,可以直接引用spring的變量配置,于是能夠根據(jù)工程配置文件(如application.yml)里面的配置對(duì)log進(jìn)行動(dòng)態(tài)配置。
但是在unit?test下,一直無(wú)法正常獲取到變量,應(yīng)該是springboot test沒有對(duì)這種情況進(jìn)行支持。嘗試了很多方法,最后都無(wú)效。
考慮到unit?test對(duì)于日志需求不多,只需要考慮簡(jiǎn)單輸出即可。于是在test目錄下創(chuàng)建resources目錄,然后創(chuàng)建一個(gè)log4j2-spring.xml文件,此時(shí)spring?test會(huì)自動(dòng)使用該配置文件。然后該配置文件里面去除掉spring變量的引用即可。
7、在寫一個(gè)測(cè)試類的時(shí)候,添加了@Test注解,但是IDEA卻一直提示無(wú)法執(zhí)行該測(cè)試方法(沒有執(zhí)行的小圖標(biāo))??戳艘粫?huì)才發(fā)現(xiàn),原來(lái)是用錯(cuò)了@Test注解類。
正常應(yīng)該用org.junit.jupiter.api.Test類,而剛才使用了org.junit.Test。修改了一下,一切正常。于是好奇為啥Junit會(huì)提供兩個(gè)Test注解類。查看官方文檔,才發(fā)現(xiàn),原來(lái)是因?yàn)橐粋€(gè)是Junit4,一個(gè)是Junit5的。繼續(xù)查看文檔,才發(fā)現(xiàn)SpringBoot Test包為了兼容Junit4,自動(dòng)添加了對(duì)vintage引擎(Junit4版本的引擎,Junit5的引擎是jupiter)的依賴(2.4.0以后的版本將不再默認(rèn)添加vintage引擎),同時(shí)看到官方文檔里面有默認(rèn)推薦配置,需要將vintage引擎依賴包排除。于是修改pom配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
改完之后發(fā)現(xiàn)很多測(cè)試類報(bào)錯(cuò)了,原來(lái)之前寫的很多類都是用的Junit4的Test注解。逐個(gè)類進(jìn)行處理,到時(shí)沒有啥特別的。
需要注意的是,對(duì)于Junit5,通過(guò)SpringBootTest注解,已經(jīng)不再需要@RunWith注解了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-720711.html
而對(duì)于部分不需要SpringBootTest的情況,@RunWith注解也發(fā)生了變化,需要改成:@ExtendWith(SpringExtension.class)即可。其他變動(dòng)都是無(wú)感的。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-720711.html
到了這里,關(guān)于SpringBoot下進(jìn)行單元測(cè)試的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!