spring boot单元测试
做一個(gè)穩(wěn)健的開(kāi)發(fā),寫(xiě)一首漂亮的單元測(cè)試是少不了的
首先要分清幾個(gè)概念:測(cè)試方法、測(cè)試類(lèi)、測(cè)試集、測(cè)試運(yùn)行器。
- 測(cè)試方法就是用 @Test 注解的一些函數(shù)。
- 測(cè)試類(lèi)是包含一個(gè)或多個(gè)測(cè)試方法的一個(gè) XxTest.java 文件
- 測(cè)試集是一個(gè)suite, 可能包含多個(gè)測(cè)試類(lèi)。
- 測(cè)試運(yùn)行器則決定了用什么方式偏好去運(yùn)行這些測(cè)試集/類(lèi)/方法。
Spring Boot 模塊獨(dú)立測(cè)試
Spring Boot 支持各模塊單獨(dú)隔離測(cè)試,例如web, db。單獨(dú)測(cè)試各模塊時(shí)不需要啟動(dòng)整個(gè)spring上下文,通過(guò)禁用一些spring boot自動(dòng)配置來(lái)實(shí)現(xiàn)。
Spring Boot Test Slices Overview and Usage
https://rieckpil.de/spring-boot-test-slices-overview-and-usage/
@WebMvcTest 測(cè)試web層
@WebMvcTest?測(cè)試web層,controller層,不包括service層。
@WebMvcTest 注解主要用于controller層測(cè)試,只覆蓋應(yīng)用程序的controller層,HTTP請(qǐng)求和響應(yīng)是Mock出來(lái)的,因此不會(huì)創(chuàng)建真正的連接。因此需要?jiǎng)?chuàng)建 MockMvc bean進(jìn)行模擬接口調(diào)用。
如果Controller層對(duì)Service層中的其他bean有依賴(lài)關(guān)系,那么需要使用Mock提供所需的依賴(lài)項(xiàng)。
WebMvcTest要快得多,因?yàn)槲覀冎患虞d了應(yīng)用程序的一小部分。
?
@DataJpaTest 測(cè)試jpa
@DataJpaTest?測(cè)試jpa @Repository EntityManager TestEntityManager DataSource
@DataJpaTest?注解會(huì)禁用 spring boot 的其他自動(dòng)配置,只保留 jpa 測(cè)試相關(guān)的。
默認(rèn)情況下?@DataJpaTest?注解的測(cè)試類(lèi)都是事務(wù)型的,測(cè)試方法結(jié)束后會(huì)回滾操作。
默認(rèn)情況下?@DataJpaTest?會(huì)啟動(dòng)一個(gè)內(nèi)存數(shù)據(jù)庫(kù),例如 H2 或 Derby,來(lái)代替其他數(shù)據(jù)庫(kù)。配合使用?@AutoConfigureTestDatabase?注解來(lái)自動(dòng)配置一個(gè)測(cè)試庫(kù)。
如果想加載全部spring上下文,同時(shí)使用內(nèi)存數(shù)據(jù)庫(kù),應(yīng)該使用?@SpringBootTest?搭配?@AutoConfigureTestDatabase?注解來(lái)實(shí)現(xiàn)。
@DataJpaTest?注解的測(cè)試類(lèi)中可以直接注入?TestEntityManager?來(lái)作為?EntityManager?使用。
如果想在?@DataJpaTest?外使用 TestEntityManager, 需要添加 @AutoConfigureTestEntityManager 注解
TestEntityManager NullPointerException
一開(kāi)始注入的 TestEntityManager 一直是 null,后來(lái)加上 @RunWith(SpringRunner.class) 就好了
有?@DataJpaTest?而不啟動(dòng) Spring 上下文時(shí),可以直接注入 TestEntityManager 使用。
如果不使用?@DataJpaTest?而是啟動(dòng)Spring上下文的話(huà),就沒(méi)有 TestEntityManager 實(shí)例可注入了,需要改為注入 EntityManager
Test Your Spring Boot JPA Persistence Layer With @DataJpaTest
https://rieckpil.de/test-your-spring-boot-jpa-persistence-layer-with-datajpatest/
@JdbcTest 測(cè)試jdbc
@JdbcTest?測(cè)試jdbc
@DataMongoTest 測(cè)試mongo
@DataMongoTest?測(cè)試mongo
@DataMongoTest?注解會(huì)禁用 spring boot 的其他自動(dòng)配置,只保留 mongo 測(cè)試相關(guān)的。
@JsonTest 測(cè)試json
@JsonTest?測(cè)試json序列化、反序列化
@RestClientTest 測(cè)試http客戶(hù)端
@RestClientTest?測(cè)試http客戶(hù)端
@SpringBootTest 測(cè)試springboot應(yīng)用
@SpringBootTest?測(cè)試spring boot應(yīng)用,各種service層。
@Transactional 測(cè)試數(shù)據(jù)自動(dòng)回滾
可以實(shí)現(xiàn)再springboot中使用junit編寫(xiě)單元測(cè)試,并且測(cè)試結(jié)果不影響數(shù)據(jù)庫(kù)。
@Transactional 表示該方法整體為一個(gè)事務(wù),可以用在測(cè)試類(lèi)上表示所有測(cè)試方法都回滾,或具體的 @Test 方法上。
@Rollback 表示事務(wù)執(zhí)行完回滾,支持傳入一個(gè)參數(shù)value,默認(rèn)true即回滾,false不回滾。
springboot中junit回滾
https://www.jianshu.com/p/d9d0abf317c0
使用MockMvc測(cè)試Spring MVC Controller
用到的注解:
@RunWith(SpringJUnit4ClassRunner.class): 表示使用Spring Test組件進(jìn)行單元測(cè)試;
@WebAppConfiguration: 使用這個(gè)Annotate會(huì)在跑單元測(cè)試的時(shí)候真實(shí)的啟動(dòng)一個(gè)web服務(wù),然后開(kāi)始調(diào)用Controller的Rest API,待單元測(cè)試跑完之后再將web服務(wù)停掉;
@ContextConfiguration: 指定Bean的配置文件信息,可以有多種方式,這個(gè)例子使用的是文件路徑形式,如果有多個(gè)配置文件,可以將括號(hào)中的信息配置為一個(gè)字符串?dāng)?shù)組來(lái)表示;controller,component等都是使用注解,需要注解指定spring的配置文件,掃描相應(yīng)的配置,將類(lèi)初始化等。
@TransactionConfiguration(transactionManager=”transactionManager”,defaultRollback=true)配置事務(wù)的回滾,對(duì)數(shù)據(jù)庫(kù)的增刪改都會(huì)回滾,便于測(cè)試用例的循環(huán)利用
為什么要進(jìn)行事務(wù)回滾:
1、測(cè)試過(guò)程對(duì)數(shù)據(jù)庫(kù)的操作,會(huì)產(chǎn)生臟數(shù)據(jù),影響我們數(shù)據(jù)的正確性
2、不方便循環(huán)測(cè)試,即假如這次我們將一個(gè)記錄刪除了,下次就無(wú)法再進(jìn)行這個(gè)Junit測(cè)試了,因?yàn)樵撚涗浺呀?jīng)刪除,將會(huì)報(bào)錯(cuò)。
3、如果不使用事務(wù)回滾,我們需要在代碼中顯式的對(duì)我們的增刪改數(shù)據(jù)庫(kù)操作進(jìn)行恢復(fù),將多很多和測(cè)試無(wú)關(guān)的代碼
測(cè)試類(lèi)基類(lèi)
import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext;//這個(gè)必須使用junit4.9以上才有 @RunWith(SpringJUnit4ClassRunner.class) //單元測(cè)試的時(shí)候真實(shí)的開(kāi)啟一個(gè)web服務(wù) @WebAppConfiguration //配置事務(wù)的回滾,對(duì)數(shù)據(jù)庫(kù)的增刪改都會(huì)回滾,便于測(cè)試用例的循環(huán)利用 @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true) @Transactional @ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-hibernate.xml"}) public class AbstractContextControllerTests {@Autowired protected WebApplicationContext wac; } 具體測(cè)試類(lèi)package com.pengtu.gsj;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;import org.hibernate.SessionFactory; import org.junit.Before; import org.junit.Test; import org.owasp.esapi.ESAPI; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders;import com.pengtu.gsj.controller.BannerController; import com.pengtu.gsj.dao.UserDao; import com.pengtu.gsj.entity.app.User; import com.pengtu.gsj.service.UserService;public class EsapiTest extends AbstractContextControllerTests{private MockMvc mockMvc; //該方法在每個(gè)方法執(zhí)行之前都會(huì)執(zhí)行一遍 @Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.standaloneSetup(new BannerController()).build(); }/** * perform:執(zhí)行一個(gè)RequestBuilder請(qǐng)求,會(huì)自動(dòng)執(zhí)行SpringMVC的流程并映射到相應(yīng)的控制器執(zhí)行處理; * get:聲明發(fā)送一個(gè)get請(qǐng)求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根據(jù)uri模板 和uri變量值得到一個(gè)GET請(qǐng)求方式的。另外提供了其他的請(qǐng)求的方法,如:post、put、delete等。 * param:添加request的參數(shù),如上面發(fā)送請(qǐng)求的時(shí)候帶上了了pcode = root的參數(shù)。假如使用需要發(fā)送json數(shù)據(jù)格式的時(shí)將不能使用這種 方式,可見(jiàn)后面被@ResponseBody注解參數(shù)的解決方法 * andExpect:添加ResultMatcher驗(yàn)證規(guī)則,驗(yàn)證控制器執(zhí)行完成后結(jié)果是否正確(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷); * andDo:添加ResultHandler結(jié)果處理器,比如調(diào)試時(shí)打印結(jié)果到控制臺(tái)(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷); * andReturn:最后返回相應(yīng)的MvcResult;然后進(jìn)行自定義驗(yàn)證/進(jìn)行下一步的異步處理(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷) * @throws Exception */ @Test public void getAllBanners() throws Exception{ String responseString = mockMvc.perform(get("/banner/hello") //請(qǐng)求的url,請(qǐng)求的方法是get .contentType(MediaType.APPLICATION_JSON) //數(shù)據(jù)的格式 .param("id","123456789") //添加參數(shù) ).andExpect(status().isOk()) //返回的狀態(tài)是200 .andDo(print()) //打印出請(qǐng)求和相應(yīng)的內(nèi)容 .andReturn().getResponse().getContentAsString(); //將相應(yīng)的數(shù)據(jù)轉(zhuǎn)換為字符串 System.out.println("--------返回的json = " + responseString); } }perform:執(zhí)行一個(gè)RequestBuilder請(qǐng)求,會(huì)自動(dòng)執(zhí)行SpringMVC的流程并映射到相應(yīng)的控制器執(zhí)行處理;
get:聲明發(fā)送一個(gè)get請(qǐng)求的方法。MockHttpServletRequestBuilder get(String urlTemplate, Object… urlVariables):根據(jù)uri模板和uri變量值得到一個(gè)GET請(qǐng)求方式的。另外提供了其他的請(qǐng)求的方法,如:post、put、delete等。
param:添加request的參數(shù),如上面發(fā)送請(qǐng)求的時(shí)候帶上了了pcode = root的參數(shù)。假如使用需要發(fā)送json數(shù)據(jù)格式的時(shí)將不能使用這種方式。
andExpect:添加ResultMatcher驗(yàn)證規(guī)則,驗(yàn)證控制器執(zhí)行完成后結(jié)果是否正確(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷);
andDo:添加ResultHandler結(jié)果處理器,比如調(diào)試時(shí)打印結(jié)果到控制臺(tái)(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷);
andReturn:最后返回相應(yīng)的MvcResult;然后進(jìn)行自定義驗(yàn)證/進(jìn)行下一步的異步處理(對(duì)返回的數(shù)據(jù)進(jìn)行的判斷)
使用MockMvc測(cè)試Spring mvc Controller
https://blog.csdn.net/zhang289202241/article/details/62042842
SpringRunner和SpringJUnit4ClassRunner
SpringRunner 是 SpringJUnit4ClassRunner 的別名,兩者沒(méi)有任何區(qū)別
What is the difference between SpringJUnit4ClassRunner and SpringRunner
https://stackoverflow.com/questions/47446529/what-is-the-difference-between-springjunit4classrunner-and-springrunner
Class SpringRunner
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/junit4/SpringRunner.html
spring boot單元測(cè)試
1、要讓一個(gè)普通類(lèi)變成一個(gè)單元測(cè)試類(lèi)只需要在類(lèi)名上加入?@SpringBootTest?和?@RunWith(SpringRunner.class)?兩個(gè)注釋即可。
2、在測(cè)試方法上加上?@Test?注釋。
Spring Boot 單元測(cè)試詳解+實(shí)戰(zhàn)教程
https://www.cnblogs.com/javastack/p/9150408.html
@SpringBootTest
@SpringBootTest?為 springApplication 創(chuàng)建上下文并支持 SpringBoot 特性
@SpringBootTest?注解告訴 SpringBoot 去尋找一個(gè)主配置類(lèi)(例如帶有?@SpringBootApplication?的配置類(lèi)),并使用它來(lái)啟動(dòng) Spring 應(yīng)用程序上下文。SpringBootTest 加載完整的應(yīng)用程序并注入所有可能的bean,因此速度會(huì)很慢。
在這種情況下,不需要?jiǎng)?chuàng)建 MockMvc bean,可以直接通過(guò) RestTemplate 進(jìn)行請(qǐng)求測(cè)試(或者使用 TestRestTemplate )。
使用 @SpringBootTest 的 webEnvironment 屬性定義運(yùn)行環(huán)境:
Mock(默認(rèn)): 加載 WebApplicationContext 并提供模擬的 web 環(huán)境 Servlet環(huán)境,使用此批注時(shí),不會(huì)啟動(dòng)嵌入式服務(wù)器
RANDOM_PORT: 加載 WebServerApplicationContext 并提供真實(shí)的 web 環(huán)境,嵌入式服務(wù)器,監(jiān)聽(tīng)端口是隨機(jī)的
DEFINED_PORT: 加載 WebServerApplicationContext 并提供真實(shí)的 Web 環(huán)境,嵌入式服務(wù)器啟動(dòng)并監(jiān)聽(tīng)定義的端口(來(lái)自 application.properties 或默認(rèn)端口 8080)
NONE: 使用 SpringApplication 加載 ApplicationContext 但不提供任何Web環(huán)境
Spring Test單元測(cè)試
http://jianwl.com/2016/08/07/Spring-Test%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/
Annotation Type SpringBootTest
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html
@TestPropertySource 設(shè)置System Property
@TestPropertySource 可以用來(lái)覆蓋掉來(lái)自于系統(tǒng)環(huán)境變量、Java系統(tǒng)屬性、@PropertySource的屬性。
同時(shí)@TestPropertySource(properties=…)優(yōu)先級(jí)高于@TestPropertySource(locations=…)。
利用它我們可以很方便的在測(cè)試代碼里微調(diào)、模擬配置(比如修改操作系統(tǒng)目錄分隔符、數(shù)據(jù)源等)。
Spring、Spring Boot和TestNG測(cè)試指南 - @TestPropertySource
https://segmentfault.com/a/1190000010854607
How to set environment variable or system property in spring tests?
https://stackoverflow.com/questions/11306951/how-to-set-environment-variable-or-system-property-in-spring-tests
@ClassRule 和 @Rule
junit中的 @ClassRule,可以在所有類(lèi)方法開(kāi)始前進(jìn)行一些初始化調(diào)用,比如創(chuàng)建臨時(shí)文件,
package com.jdriven;import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder;import java.io.File; import java.io.IOException;public class JUnitClassRuleTest {@ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder();public static File tempFile;@BeforeClass public static void createTempFile() throws IOException { tempFile = temporaryFolder.newFile("tempFile.txt"); }@Test public void testJUnitClassRule_One() { //Your test should go here, which uses tempFile }@Test public void testJUnitClassRule_Two() { //Your test should go here and uses the same tempFile } }其中,@ClassRule中指定創(chuàng)建臨時(shí)文件夾,這是在所有的測(cè)試方法前會(huì)創(chuàng)建文件夾,并且會(huì)在所有測(cè)試完成后,遞歸刪除其下的子目錄和子文件夾。
@Rule 是方法級(jí)別的,每個(gè)測(cè)試方法執(zhí)行時(shí)都會(huì)調(diào)用被注解的Rule,而@ClassRule是類(lèi)級(jí)別的,在執(zhí)行一個(gè)測(cè)試類(lèi)的時(shí)候只會(huì)調(diào)用一次被注解的Rule
junit中的@classrule,@rule
http://jackyrong.iteye.com/blog/2193451
@RunWith
@RunWith指定JUnit使用的單元測(cè)試執(zhí)行類(lèi),使用@RunWith注解可以改變JUnit的默認(rèn)執(zhí)行類(lèi)
@Runwith放在測(cè)試類(lèi)名之前,用來(lái)確定這個(gè)類(lèi)怎么運(yùn)行的。也可以不標(biāo)注,會(huì)使用默認(rèn)運(yùn)行器。
常見(jiàn)的運(yùn)行器有:
-
參數(shù)化運(yùn)行器
@RunWith(Parameterized.class) 參數(shù)化運(yùn)行器,配合@Parameters使用junit的參數(shù)化功能 -
測(cè)試集運(yùn)行器
@RunWith(Suite.class)
@SuiteClasses({ATest.class,BTest.class,CTest.class})
測(cè)試集運(yùn)行器配合使用測(cè)試集功能 -
junit4的默認(rèn)運(yùn)行器
@RunWith(JUnit4.class)
junit4的默認(rèn)運(yùn)行器 -
兼容junit3.8的運(yùn)行器
@RunWith(JUnit38ClassRunner.class)
用于兼容junit3.8的運(yùn)行器 -
SpringJUnit4ClassRunner
@RunWith(SpringJUnit4ClassRunner.class)集成了spring的一些功能
junit常用注解詳細(xì)說(shuō)明
http://www.cnblogs.com/tobey/p/4837495.html
使用RunWith注解改變JUnit的默認(rèn)執(zhí)行類(lèi),并實(shí)現(xiàn)自已的Listener
http://blog.csdn.net/fenglibing/article/details/8584602
@Test
在junit3中,是通過(guò)對(duì)測(cè)試類(lèi)和測(cè)試方法的命名來(lái)確定是否是測(cè)試,且所有的測(cè)試類(lèi)必須繼承junit的測(cè)試基類(lèi)。在junit4中,定義一個(gè) 測(cè)試方法變得簡(jiǎn)單很多,只需要在方法前加上@Test就行了。
注意:測(cè)試方法必須是public void,即公共、無(wú)返回?cái)?shù)據(jù)。可以?huà)伋霎惓!?/p>
junit常用注解詳細(xì)說(shuō)明
http://www.cnblogs.com/tobey/p/4837495.html
@ContextConfiguration
@ContextConfiguration?注解用于指定 spring 配置文件所在的路徑,有以下幾個(gè)常用的屬性:
locations 配置文件路徑
locations?可以通過(guò)該屬性手工指定 Spring 配置文件所在的位置,可以指定一個(gè)或多個(gè) Spring 配置文件。如下所示:
@ContextConfiguration(locations = {"xx/yy/beans1.xml","xx/yy/beans2.xml"}) @ContextConfiguration(locations = "classpath*:spring-ctx-*.xml")inheritLocations 是否繼承父類(lèi)配置
inheritLocations:是否要繼承父測(cè)試用例類(lèi)中的 Spring 配置文件,默認(rèn)為 true。如下面的例子:
@ContextConfiguration(locations={"base-context.xml"}) public class BaseTest { // ... } @ContextConfiguration(locations={"extended-context.xml"}) public class ExtendedTest extends BaseTest { // ... }如果 inheritLocations 設(shè)置為 false,則 ExtendedTest 僅會(huì)使用 extended-context.xml 配置文件,否則將使用 base-context.xml 和 extended-context.xml 這兩個(gè)配置文件。
classes 指定配置類(lèi)
classes 屬性可指定一些 Configuration 配置類(lèi),例如:
@SpringBootTest @ContextConfiguration(classes = MyConfiguration.class) public class ConsulLockTest { }
Spring基于注解TestContext 測(cè)試框架使用詳解
http://blog.csdn.net/yaerfeng/article/details/25368447
Spring 注解學(xué)習(xí)手札(六) 測(cè)試
http://snowolf.iteye.com/blog/588351
總結(jié)
以上是生活随笔為你收集整理的spring boot单元测试的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 操作系统系列「一」OPERATING S
- 下一篇: 【安全系列之加密算法】常用安全的加密算法