目錄html
本文主要介紹單元測試、集成測試相關的概念、技術實現以及最佳實踐。java
本文的demo是基於Java語言,Spring Boot構建的web應用。測試框架使用Junit,模擬框架使用mockito。mysql
以前曾經總結過一篇文章:基於spring-boot的應用程序的單元測試方案,可是當時只是從技術實現的角度去研究單元測試,不少概念沒有搞清楚。本文在從新梳理脈絡,豐富概念的基礎上,整合了前文的大部份內容,可是有一部分幾乎在實踐中用不到的內容就被刪去了。git
在個人我的wiki站點,能夠得到更好的閱讀體驗喔:基於spring-boot的應用程序的單元+集成測試方案程序員
測試領域有不少場景,好比單元測試,集成測試,系統測試,冒煙測試,迴歸測試,端到端測試,功能測試等。測試的分類方式各有不一樣,一些測試場景也可能存在重疊。具體這些場景的概念和區別,你們能夠閱讀文末給出的參考資料。github
這裏主要以程序員的視角談一下我理解的單元測試和集成測試。web
單元測試是編寫單元測試類,針對類級別的測試。好比使用Junit框架,針對一個類,寫一個測試類,測試目標類的大部分主要方法。redis
須要注意單元測試的級別是類。項目當中,類之間的依賴調用是很常見的事,若是你要測試一個類,而這個目標類又調用了另外一個類,那麼在測試時就沒有遵照「在一個類範圍內進行測試」,天然算不得單元測試。spring
如上圖所示,假設A,B,C,D四個類之間存在上述的依賴關係,咱們要測試類A,那麼如何遵照「在類A的範圍內測試」?sql
這就是模擬框架要解決的問題了,經過模擬B和C,咱們能夠在測試A的時候,調用B和C的模擬對象,而不是實際的B和C。下文會有詳細介紹。
若是在測試時超脫一個類的範圍,那就能夠稱爲集成測試。如上圖所示,你能夠測試類A,它會直接或間接調用其餘三個類,這就能夠叫作集成測試。若是你去測試類C,由於它會調用D,也能夠稱爲集成測試。
若是純粹按照單元測試的概念,把這個工做代入到一個大型的項目,成百上千的類須要編寫測試類,並且類之間的依賴須要編寫模擬代碼。這樣的工做太過龐大,對項目來講應該是得不償失的。
我推薦的作法是識別核心代碼,或者說是重要的代碼,只對這些代碼作精細的單元測試。除此以外,都經過集成測試來覆蓋。集成測試時優先從最頂層開始,讓測試天然流下來。而後根據代碼測試覆蓋報告,再進行補刀。
此處介紹的mock和stub,是做者基於mockito框架的理解,行業內對這兩個概念的定義和此處的理解可能有所出入。做者不追求對概念有「專業的定義」或者「精確的定義」,若是讀者有此追求,可另外查閱其餘資料。
上文講到,在作單元測試的時候,須要屏蔽目標類的依賴,mock和stub就是這種操做涉及到的兩個概念。
在項目代碼中,常常會涉及依賴多個外部資源的狀況,好比數據庫、微服務中的其餘服務。這表示在測試的時候須要先作不少準備工做,好比準備數據庫環境,好比先把依賴的服務run起來。
另外,還須要考慮消除測試的反作用,以使測試具有冪等性。好比若是測試會修改數據庫,那麼是否會影響二次測試的結果,或者影響整個測試環境?
對外部的資源依賴進行模擬,是一個有效的解決方案。即測試時不是真正的操做外部資源,而是經過自定義的代碼進行模擬操做。咱們能夠對任何的依賴進行模擬,從而使測試的行爲不須要任何準備工做或者不具有任何反作用。
在這個大環境下,能夠解釋mock和stub的含義。當咱們在測試時,若是隻關心某個操做是否執行過,而不關心這個操做的具體行爲,這種技術稱爲mock。
好比咱們測試的代碼會執行發送郵件的操做,咱們對這個操做進行mock;測試的時候咱們只關心是否調用了發送郵件的操做,而不關心郵件是否確實發送出去了。
另外一種狀況,當咱們關心操做的具體行爲,或者操做的返回結果的時候,咱們經過執行預設的操做來代替目標操做,或者返回預設的結果做爲目標操做的返回結果。這種對操做的模擬行爲稱爲stub(打樁)。
好比咱們測試代碼的異常處理機制是否正常,咱們能夠對某處代碼進行stub,讓它拋出異常。再好比咱們測試的代碼須要向數據庫插入一條數據,咱們能夠對插入數據的代碼進行stub,讓它始終返回1,表示數據插入成功。
當咱們進行單元測試的時候,咱們但願在spring容器中只實例化測試目標類的實例。
假設咱們的測試目標以下:
@Service public class CityService { @Autowired private CityMapper cityMapper; public List<City> getAllCities() { return cityMapper.selectAllCities(); } public void save(City city) { cityMapper.insert(city); } }
咱們能夠這樣編寫測試類:
@RunWith(SpringRunner.class) @SpringBootTest public class CityServiceUnitTest { @SpringBootApplication(scanBasePackages = "com.shouzheng.demo.web") static class InnerConfig { } @Autowired private CityService cityService; @MockBean private CityMapper cityMapper; @Test public void testInsert() { City city = new City(); cityMapper.insert(city); Mockito.verify(cityMapper).insert(city); } @Test public void getAllCities() { City city = new City(); city.setId(1L); city.setName("杭州"); city.setState("浙江"); city.setCountry("CN"); Mockito.when(cityMapper.selectAllCities()) .thenReturn(Collections.singletonList(city)); List<City> result = cityService.getAllCities(); Assertions.assertThat(result.size()).isEqualTo(1); Assertions.assertThat(result.get(0).getName()).isEqualTo("杭州"); } }
@RunWith
註解聲明測試是在spring環境下運行的,這樣就能夠啓用Spring的相關支持。
@SpringBootTest
註解負責掃描配置來構建測試用的Spring上下文環境。它默認搜索@SpringBootConfiguration
類,除非咱們經過classes屬性指定配置類,或者經過自定義內嵌的@Configuration
類來指定配置。如上面的代碼,就是經過內嵌類來自定義配置。
@SpringBootApplication
擴展自@Configuration
,其scanBasePackages屬性指定了掃描的根路徑。確保測試目標類在這個路徑下,並且須要明白這個路徑下的全部bean都會被實例化。雖然咱們已經儘量的縮小了實例化的範圍,可是咱們沒有避免其餘無關類的實例化開銷。
即便如此,這種方案依然被我看做是最佳的實踐方案,由於它比較簡單。若是咱們追求「只實例化目標類」,那麼可使用下面的方式聲明內嵌類:
@Configuration @ComponentScan(value = "com.shouzheng.demo.web", useDefaultFilters = false, includeFilters = @ComponentScan.Filter( type = FilterType.REGEX, pattern = {"com.shouzheng.demo.web.CityService"}) ) static class InnerConfig { }
@ComponentScan
負責配置掃描Bean的方案,value屬性指定掃描的根路徑,useDefaultFilters屬性取消默認的過濾器,includeFilters屬性自定義了一個過濾器,這個過濾器設定爲要掃描模式匹配的類。
@ComponentScan默認的過濾器會掃描@Component,@Repository,@Service,@Controller;若是不由用默認過濾器,自定義過濾器的效果是在默認過濾器的基礎上追加更多的bean。即咱們要限定只實例化某個特定的bean,就須要把默認的過濾器禁用。
能夠看到,這種掃描策略配置,會顯得複雜不少。
@Autowired
負責注入依賴的bean,在這裏注入的是測試目標bean。
@MockBean
負責聲明這是一個模擬的bean。在進行單元測試時,須要將測試目標的全部依賴bean聲明爲模擬的bean,這些模擬的bean將被注入測試目標bean。
在testInsert方法中,咱們執行了cityMapper.insert
,這只是模擬的執行了,實際上什麼也沒作。接下來咱們調用Mockito.verify
,目的是驗證cityMapper.insert
執行了。這正對應了上文中對Mock概念的解釋,咱們只關心它是否執行了。
須要注意的是,驗證的內容同時包括參數是否一致。若是實際調用時的傳參和驗證時指定的參數不一致,則驗證失敗,以致於測試失敗。
在getAllCities方法中,咱們使用Mockito.when
對cityMapper.selectAllCities
方法進行打樁,設定當方法被調用時,直接返回咱們預設的數據。這也對應了上文中對Stub概念的解釋。
注意:只能對mock對象進行stub。
Controller是一類特殊的bean,這類bean除了顯式的依賴,還有一些系統組件的依賴。好比消息轉換組件,負責將方法的返回結果轉換成能夠寫的HTTP消息。因此,咱們沒法像測試上文那樣對其單獨實例化。
Spring提供了特定的註解,配置用於測試Controller的上下文環境。
例如咱們要測試的controller以下:
@RestController public class CityController { @Autowired private CityService cityService; @GetMapping("/cities") public ResponseEntity<?> getAllCities() { List<City> cities = cityService.getAllCities(); return ResponseEntity.ok(cities); } @PostMapping("/city") public ResponseEntity<?> newCity(@RequestBody City city) { cityService.save(city); return ResponseEntity.ok(city); } }
咱們能夠這樣編寫測試類:
@RunWith(SpringRunner.class) @WebMvcTest(CityController.class) public class CityControllerUnitTest { @Autowired private MockMvc mvc; @MockBean private CityService service; @Test public void getAllCities() throws Exception { City city = new City(); city.setId(1L); city.setName("杭州"); city.setState("浙江"); city.setCountry("中國"); Mockito.when(service.getAllCities()). thenReturn(Collections.singletonList(city)); mvc.perform(MockMvcRequestBuilders.get("/cities")) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("杭州"))); } }
@WebMvcTest
是特定的註解,它的職責和@SpringBootTest
相同,但它只會實例化Controller。默認實例化全部的Controller,也能夠指定只實例化某一到多個Controller。
除此以外,@WebMvcTest
還會實例化一個MockMvc的bean,用於發送http請求。
咱們一樣須要對測試目標的依賴進行模擬,即,將CityService聲明爲MockBean。
spring環境問題
@WebMvcTest
就像@SpringBootTest
同樣,默認搜索@SpringBootConfiguration
註解的類做爲配置類。通常狀況下,基於Spring-Boot的web應用,會建立一個啓動類,並使用@SpringBootApplication
,這個註解可看做@SpringBootConfiguration
註解的擴展,因此極可能會搜索到這個啓動類做爲配置。
若是項目當中有多個@SpringBootConfiguration
配置類,好比有些其餘的測試類建立了內部配置類,而且使用了這個註解。若是當前測試類沒有使用內部類,也沒有使用classes屬性指定使用哪一個配置類,就會由於找到了多個配置類而失敗。這種狀況下會有明確的錯誤提示信息。
思考當前測試類會使用哪個配置類,是一個很好的習慣。
另一個可能的問題是:若是配置類上添加了其餘的註解,好比Mybatis框架的@MapperScan
註解,那麼Spring會去嘗試實例化Mapper實例,可是由於咱們使用的是@WebMvcTest
註解,Spring不會去實例化Mapper所依賴的sqlSessionFactory等自動配置的組件,最終致使依賴註解失敗,沒法構建Spring上下文環境。
也就是說,雖然@WebMvcTest
默認只實例化Controller組件,可是它一樣也會聽從配置類的註解去作更多的工做。若是這些工做依賴於某些自動化配置bean,那麼將會出現依賴缺失。
解決這個問題的方法可能有不少種,我這邊提供一個本身的最佳實踐:
@RunWith(SpringRunner.class) @WebMvcTest(CityController.class) public class CityControllerWebLayer { @SpringBootApplication(scanBasePackages = {"com.shouzheng.demo.web"}) static class InnerConfig {} @Autowired private MockMvc mvc; @MockBean private CityService service; }
這個方案,是經過使用內部類來自定義配置。內部類只有一個@SpringBootApplication
註解,指定了掃描的根路徑,以縮小bean的掃描範圍。
就像測試controller同樣,持久層的單元測試也有專門的註解支持。
持久層的技術有多種,Spring提供了@JdbcTest
來支持經過spring的JdbcTemplate進行持久化的測試,提供了@DataJpsTest
支持經過JPA技術進行持久化的測試。
上面的這兩個註解我沒有作過研究,由於項目中使用的是Mybatis,這裏僅介紹Mybatis提供的測試支持:@MybatisTest
。
最簡單的方式是使用內存數據庫做爲測試數據庫,這樣能夠儘可能減小測試的環境依賴。
默認的持久層測試是回滾的,即每個測試方法執行完成以後,會回滾對數據庫的修改;因此也可使用外部的數據庫進行測試,但多少會有些影響(好比序列的當前值)。
首先,添加數據庫依賴:
<!-- pom.xml --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>RELEASE</version> <scope>test</scope> </dependency>
準備數據庫初始化腳本,好比放在resources/import.sql文件中:
drop table if exists city; drop table if exists hotel; create table city (id int primary key AUTO_INCREMENT, name varchar, state varchar, country varchar); create table hotel (city int primary key AUTO_INCREMENT, name varchar, address varchar, zip varchar); insert into city (id, name, state, country) values (1, 'San Francisco', 'CA', 'US'); insert into hotel(city, name, address, zip) values (1, 'Conrad Treasury Place', 'William & George Streets', '4001')
須要在配置文件中指定腳本文件的位置:
spring.datasource.schema=classpath:import.sql
例如咱們要測試以下的Mapper接口:
@Mapper public interface CityMapper { City selectCityById(int id); List<City> selectAllCities(); int insert(City city); }
咱們能夠這樣編寫測試類:
@RunWith(SpringRunner.class) @MybatisTest @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CityMapperUnitTest { @SpringBootApplication(scanBasePackages = {"com.shouzheng.demo.mapper"}) static class InnerConfig {} private static Logger LOG = LoggerFactory.getLogger(CityMapperUnitTest.class); @Autowired private CityMapper cityMapper; @Before @After public void printAllCities() { List<City> cities = cityMapper.selectAllCities(); LOG.info("{}", cities); } @Test // @Rollback(false) // 禁止回滾 public void test1_insert() throws Exception { City city = new City(); city.setName("杭州"); city.setState("浙江"); city.setCountry("CN"); cityMapper.insert(city); LOG.info("insert a city {}", city); } @Test public void test2_doNothing() { } }
@MybatisTest
搜索配置類的邏輯和@SpringBootTest
、@WebMvcTest
相同,爲了不Spring環境問題(上文在測試Controller一節中介紹過),這裏直接使用內部類進行配置。
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
用來指定測試方法的執行順序,這是爲了觀察事務回滾的效果。
若是將test1_insert方法上的@Rollback(false)
註釋放開,事務不會回滾,test2_doNothing方法以後打印輸出的內容會包含test1_insert方法裏插入的數據。
反之,若是註釋掉,事務回滾,test2_doNothing方法以後打印輸出的內容不包含test1_insert方法裏插入的數據。
首先,添加對應的數據庫驅動依賴,以及數據源配置。好比使用mysql外部數據庫:
<!-- pom.xml --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-jdbc.version}</version> </dependency>
# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/test?autoReconnect=true&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver
而後配置測試類,惟一不一樣的是,在測試類上要多加一個@AutoConfigureTestDatabase
註解:
@RunWith(SpringRunner.class) @MybatisTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public class CityMapperTest2 { @SpringBootApplication(scanBasePackages = {"com.shouzheng.demo.mapper"}) static class InnerConfig {} @Autowired private CityMapper cityMapper; // ... }
這樣,測試的時候就會使用咱們配置的數據庫進行測試,而不是使用內存數據庫。
測試持久層時,默認是回滾的。能夠在具體的測試方法上添加@Rollback(false)
來禁止回滾,也能夠在測試類上添加。
集成測試時會超脫一個類的範圍,咱們須要保證自測試目標類及如下的依賴類,都可以在spring容器中被實例化,最簡單的方式莫過於構建完整的spring上下文。雖然這樣一來,會有不少和測試目標無關的類也會被實例化,可是咱們省去了精心設計初始化bean的工夫,並且也間接的達到了「測試構建完整的spring上下文」的目的。
例如咱們以上文中介紹到的controller爲測試目標,測試newCity請求。測試類以下:
@RunWith(SpringRunner.class) @SpringBootTest(classes = DemoTestSpringBootApplication.class) @AutoConfigureMockMvc @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CityControllerWithRollbackTest { private static Logger LOG = LoggerFactory.getLogger(CityControllerWithRollbackTest.class); @Autowired private MockMvc mockMvc; @Before @After public void getAllCities() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/cities")) .andDo(result -> { String content = result.getResponse().getContentAsString(); LOG.info("cities = {}", content); }); } @Test @Transactional // @Rollback(false) public void test1_insertCity() throws Exception { LOG.info("insert a city"); mockMvc.perform(MockMvcRequestBuilders.post("/city") .contentType(MediaType.APPLICATION_JSON) .content("{\"name\": \"杭州\", \"state\": \"浙江\", \"country\": \"中國\"}")) .andExpect(MockMvcResultMatchers.status().isOk()); } /** * 爲了觀察數據庫是否回滾 */ @Test public void test2_doNothind() { } }
這段代碼主要測試新增數據記錄的請求,並在測試先後分別請求並打印當前的數據記錄集。咱們能夠看到,在test1_insertCity方法運行以後打印的數據集,會比在此以前打印的數據集多一條記錄,而這條記錄正是咱們申請新增的數據記錄。
test2_doNothind是一個輔助的測試方法,在完成test1_insertCity方法以後,開始執行test2_doNothind測試。而測試前的打印數據記錄集的行爲,可讓咱們觀察到test1_insertCity測試中新增的數據是否發生回滾。
集成測試時使用@SpringBootTest
註解,指定配置類爲項目啓動類。若是咱們的項目是基於spring-cloud的微服務環境,那麼也可使用內部配置類來減小服務註冊等相關的配置。
@AutoConfigureMockMvc
是爲了實例化MockMvc實例,用來發送http請求。
實驗證實,集成測試依然能夠支持數據庫操做回滾,方案就是在測試方法上使用@Transactional
註解,標識事務性操做。同時,咱們依然可使用@Rollback
來設置是否回滾。
集成測試不是非要從最頂層開始測試,咱們也能夠從service層開始測試:
@RunWith(SpringRunner.class) @SpringBootTest(classes = {DemoTestSpringBootApplication.class}) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class CityServiceWithRollbackTest { private static Logger LOG = LoggerFactory.getLogger(CityServiceWithRollbackTest.class); @Autowired private CityService cityService; @Before @After public void printAllCities() { List<City> cities = cityService.getAllCities(); LOG.info("{}", cities); } @Test @Transactional public void test1_insert() { City city = new City(); city.setName("杭州"); city.setState("浙江"); city.setCountry("CN"); cityService.save(city); LOG.info("insert a new city {}", city); } @Test public void test2_doNothind() { } }
這段代碼的測試方案和上文的controller集成測試方案相同,都是測試新增操做,並在測試先後打印當前數據集,來演示是否支持事務回滾。
在spring項目的測試類中,咱們能夠對任意的類進行mock,以下面這樣:
@RunWith(SpringRunner.class) @SpringBootTest public class CityServiceUnitTest { @MockBean private CityMapper cityMapper; ... }
定義一個field,對其添加@MockBean
註解,就聲明瞭對應類型的一個mock bean。若是spring上下文中已經存在對應類型的bean,將會被mock bean覆蓋掉。
默認的狀況下,mock bean的全部方法都是透明的:什麼也不作,直接返回對應類型的默認值。聲明返回引用類型的方法,將直接返回null;聲明返回基本類型的方法,直接返回相應的默認值;聲明無返回的方法,那更是透明的。
mock的做用對靜態方法無效,靜態方法會被實際調用。因此建議不要在靜態方法中進行資源相關的處理,不然將沒法進行模擬測試。好比,使用靜態方法封裝數據庫操做的行爲是很差的。
如上文所述,Mock的使用場景是咱們只關注對應的方法是否執行了,而不關心實際的執行效果。實際代碼中,咱們能夠按照下面的方式使用:
@Test @Transactional public void test1_insert() { City city = new City(); city.setName("杭州"); city.setState("浙江"); city.setCountry("CN"); cityService.save(city); Mockito.verify(cityMapper).insert(city); LOG.info("insert a new city {}", city); }
Mockito.verify
開始的一行,用來驗證做爲mock bean的cityMapper的insert方法會被執行,並且參數爲city。若是方法沒有被調用,或者實際調用時的傳參不一致,都會致使測試失敗。
好比,若是改爲Mockito.verify(cityMapper).insert(new City());
,將會拋出下面的異常:
Argument(s) are different! Wanted: cityMapper bean.insert(null,null,null,null); -> at com.shouzheng.demo.web.CityServiceWithRollbackTest.test1_insert(CityServiceWithRollbackTest.java:56) Actual invocation has different arguments: cityMapper bean.insert(null,杭州,浙江,CN); -> at com.shouzheng.demo.web.CityService.save(CityService.java:26) Comparison Failure: Expected :cityMapper bean.insert(null,null,null,null); Actual :cityMapper bean.insert(null,杭州,浙江,CN);
在Mock的基礎上更進一步,若是咱們關注方法的返回結果,或者咱們但願方法能有預約的行爲,使得測試按照咱們預期的方向進行,那麼咱們須要對mock bean的某些方法進行stub,讓這些方法在參數知足某個條件的狀況下,給咱們預設的響應。
實際代碼中,咱們只能對mock bean的方法進行stub,不然獲得下面的異常:
org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'. For example: when(mock.getArticles()).thenReturn(articles); Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods. Those methods *cannot* be stubbed/verified. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object.
咱們能夠按照下面的方式,讓它返回預設的結果:
Mockito.when(cityMapper.selectAllCities()) .thenReturn(Collections.singletonList(city));
或者拋出預設的異常(若是咱們檢測異常處理代碼的話):
Mockito.when(cityMapper.selectAllCities()) .thenThrow(new RuntimeException("test"));
或者去執行實際的方法:
when(mock.someMethod()).thenCallRealMethod();
注意,調用真實的方法有違mock的本義,應該儘可能避免。若是要調用的方法中調用了其餘的依賴,須要自行注入其餘的依賴,不然會空指針。
若是咱們但願它可以執行預設的操做,好比打印咱們傳入的參數,或者修改咱們傳入的參數,咱們能夠按照下面的方式實現:
Mockito.when(cityMapper.insert(Mockito.any())) .then(invocation -> { LOG.info("arguments are {}", invocation.getArguments()); return 1; });
咱們能夠指定明確的參數匹配條件,或者使用模式匹配:
@RunWith(SpringRunner.class) @SpringBootTest public class MathServiceTest { @Configuration static class ConfigTest {} @MockBean private MathService mathService; @Test public void testDivide() { Mockito.when(mathService.divide(4, 2)) .thenReturn(2); Mockito.when(mathService.divide(8, 2)) .thenReturn(4); Mockito.when(mathService.divide(Mockito.anyInt(), Mockito.eq(0))) // 必須同時用模式 .thenThrow(new RuntimeException("error")); Assertions.assertThat(mathService.divide(4, 2)) .isEqualTo(2); Assertions.assertThat(mathService.divide(8, 2)) .isEqualTo(4); Assertions.assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> { mathService.divide(3, 0); }) .withMessageContaining("error"); } }
上面的測試可能有些奇怪,mock的對象也同時做爲測試的目標。這是由於咱們的目的在於介紹mock,因此簡化了測試流程。
注意,若是咱們對方法的其中一個參數使用了模式,其餘的參數都須要使用模式。好比下面這句:
Mockito.when(mathService.divide(Mockito.anyInt(), Mockito.eq(0)))
,咱們的本意是Mockito.when(mathService.divide(Mockito.anyInt(), 0))
,可是咱們不得不爲第二個參數使用模式。
註解 | 說明 |
---|---|
@RunWith |
junit的註解,經過這個註解使用SpringRunner.class ,可以將junit和spring進行集成。後續的spring相關注解纔會起效。 |
@SpringBootTest |
spring的註解,經過掃描應用程序中的配置來構建測試用的Spring上下文。 |
@AutoConfigureMockMvc |
spring的註解,可以自動配置MockMvc 對象實例,用來在模擬測試環境中發送http請求。 |
@WebMvcTest |
spring的註解,切片測試的一種。使之替換@SpringBootTest 能將構建bean的範圍限定於web層,可是web層的下層依賴bean,須要經過mock來模擬。也能夠經過參數指定只實例化web層的某一個到多個controller。具體可參考Auto-configured Spring MVC Tests。 |
@RestClientTest |
spring的註解,切片測試的一種。若是應用程序做爲客戶端訪問其餘Rest服務,能夠經過這個註解來測試客戶端的功能。具體參考Auto-configured REST Clients。 |
@MybatisTest |
mybatis按照spring的習慣開發的註解,切片測試的一種。使之替換@SpringBootTest ,可以將構建bean的返回限定於mybatis-mapper層。具體可參考mybatis-spring-boot-test-autoconfigure。 |
@JdbcTest |
spring的註解,切片測試的一種。若是應用程序中使用Jdbc做爲持久層(spring的JdbcTemplate ),那麼可使用該註解代替@SpringBootTest ,限定bean的構建範圍。官方參考資料有限,可自行網上查找資料。 |
@DataJpaTest |
spring的註解,切片測試的一種。若是使用Jpa做爲持久層技術,可使用這個註解,參考Auto-configured Data JPA Tests。 |
@DataRedisTest |
spring的註解,切片測試的一種。具體內容參考Auto-configured Data Redis Tests。 |