內(nèi)容是:Spring Boot 中的單元測
前言
何為單元測試
單元測試的目的: 測試當(dāng)前所寫的代碼是否是正確的, 例如輸入一組數(shù)據(jù), 會輸出期望的數(shù)據(jù); 輸入錯誤數(shù)據(jù), 會產(chǎn)生錯誤異常等.
在單元測試中, 我們需要保證被測系統(tǒng)是獨立的(SUT
沒有任何的 DOC), 即當(dāng)被測系統(tǒng)通過測試時, 那么它在任何環(huán)境下都是能夠正常工作的. 編寫單元測試時, 僅僅需要關(guān)注單個類就可以了.
而不需要關(guān)注例如數(shù)據(jù)庫服務(wù), Web 服務(wù)等組件,。
背景
進(jìn)行過JavaWeb
開發(fā)的同學(xué)都了解,,在進(jìn)行后臺開發(fā)時不僅需要完成系統(tǒng)功能的開發(fā),為了保證系統(tǒng)的健壯性還要同步編寫對應(yīng)的單元測試類,?;?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Spring Boot開發(fā)的項目中的test包用于存放單元測試類,同時也提供了對應(yīng)的注解來進(jìn)行單元測試的編寫,,本文結(jié)合Mock對Spring Boot
中的單元測試進(jìn)行總結(jié),。
環(huán)境:JDK1.8+
、Spring Boot
,、mockito
,。
單元測試的引入
在Spring Boot
中引入單元測試只需在pom文件中加入如下依賴,其中提供了JUnit、SpringBoot Test等常見單元測試庫,。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.0.111-beta</version>
</dependency>
單元測試的創(chuàng)建
每個單元測試類對應(yīng)項目中的一個程序類,,每個單元測試方法對應(yīng)程序類中的一個方法,為保證所測試方法的正確性,,至少需要設(shè)計四個以上的測試用例,,包含:正確用例、錯誤用例和邊界用例,。編寫的注釋事項如下:
- 測試類的位置位于項目test包下,包的層級結(jié)構(gòu)與項目相同,;
- 測試類的命名規(guī)則通常為 xxxTest.java,,其中xxx表示待測試類名;
- 測試類中方法命名規(guī)則為testXxx,,其中Xxx表示待測試方法名 ,;
話不多說,,咱們直接開干,。
常用注解
當(dāng)下是注解盛行時代,我們先來了解一下相關(guān)的幾個注解,。
注解 | 說明 |
---|
@RunWith | 更改測試運行器 ,, 缺省值org.junit.runner.Runner |
@Before | 初始化方法,執(zhí)行當(dāng)前測試類的每個測試方法前執(zhí)行 |
@Test | 測試方法,,在這里可以測試期望異常和超時時間 |
@Test(timeout = 10000) | 超時測試方法,,若測試方法未在指定時間內(nèi)結(jié)束則junit 自動將其標(biāo)記為失敗 |
@Transactional | 聲明式事務(wù)管理,用于需數(shù)據(jù)庫事務(wù)管理的測試方法 |
@Rollback(true) | 數(shù)據(jù)庫回滾,,避免測試數(shù)據(jù)污染數(shù)據(jù)庫 |
相關(guān)理論和技術(shù)點,,現(xiàn)在已經(jīng)鋪墊完成,下面,,我們使用代碼來實現(xiàn),。
代碼實現(xiàn)
我們分別做三層的測試:controller、service,、dao
Service層測試
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceTest {
@Autowired
private UserService userService;
/**
* 測試獲取用戶
*/
@Test(timeOut = 300000)
@Transactional
public void testGetUser() {
UserEntity userEntity = userService.findByName("zhangSan");
Assert.assertNotNull(userEntity);
Assert.assertEquals("zhangSan", userEntity.getName());
}
}
是不是很簡單呢,?
Controller層測
controller層,也可以稱之為網(wǎng)絡(luò)請求測試,。對于網(wǎng)絡(luò)請求進(jìn)行測試的情形多見于應(yīng)用的Controller層,。Spring測試框架提供MockMvc
對象,可以在不需要客戶端-服務(wù)端請求的情況下進(jìn)行Web測試.
測試開始之前需要建立測試環(huán)境,,setup
方法被@Before
修飾,。通過MockMvcBuilders
工具,創(chuàng)建一個MockMvc
對象。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
class UserControllerTest {
@Autowired
private UserController userController ;
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setup(){
mockMvc = MockMvcBuilders.standaloneSetup(userController).build;
}
/**
* 獲取用戶列表
*/
@Test(timeOut = 300000)
public void testGetUserList() throws Exception {
String url = "/user/getUserList";
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
Assert.assertNotNull(mvcResult);
}
}
DAO層測試
由于DAO
層的方法直接操作數(shù)據(jù)庫,為避免測試數(shù)據(jù)對數(shù)據(jù)庫造成污染,,使用注解@Transactional
和@Rollback
在測試完成后對測試數(shù)據(jù)進(jìn)行回滾,。
@RunWith(SpringRunner.class)
@SpringBootTest
public class ScoreControllerTestNew {
@Autowired
private UserDao userDao;
/**
* 測試插入數(shù)據(jù)
*/
@Test
@Rollback(value = true)
@Transactional
public void testInsert() {
User userZhang = new User();
userZhang.setName("zhangSan");
userZhang.setAge(23);
userZhang.setGender(0);
userZhang.setEmail("[email protected]");
int n = userDao.insert(userZhang);
Assert.assertEquals(1, n);
}
}
到此,關(guān)于三個層面的測試就已經(jīng)搞定了,,下面我們來看看,如何使用Mockito
模擬數(shù)據(jù)庫操作,。
使用Mockito模擬數(shù)據(jù)庫操作
前面在介紹web請求測試時使用了Mock技術(shù),,該技術(shù)常用于被測試模塊(方法)依賴于外部系統(tǒng)(web服務(wù)、中間件或是數(shù)據(jù)庫)時,。
Mock
的中文譯為仿制的,,模擬的,虛假的,。對于測試框架來說,,即構(gòu)造出一個模擬/虛假的對象,使我們的測試能順利進(jìn)行下去,。
Mockito
是當(dāng)前最流行的 單元測試 Mock
框架,。采用 Mock
框架,我們可以 虛擬 出一個 外部依賴,,降低測試 組件 之間的 耦合度,,只注重代碼的 流程與結(jié)果,真正地實現(xiàn)測試目的,。
來于網(wǎng)絡(luò),,侵刪由于web服務(wù)或數(shù)據(jù)庫不可達(dá)時,可以對其進(jìn)行Mock,,在測試時不需要真實的模塊也可完成測試,。
常用的Mockito
方法如下:
方法 | 簡介 |
---|
Mockito.mock(classToMock) | 模擬對象 |
Mockito.when(methodCall).thenReturn(value) | 參數(shù)匹配 |
Mockito.doReturn(toBeReturned).when(mock).[method] | 參數(shù)匹配(直接執(zhí)行不判斷) |
Mockito.when(methodCall).thenAnswer(answer)) | 預(yù)期回調(diào)接口生成期望值 |
Mockito.doNothing().when(mock).[method] | 不做任何返回 |
在使用Mockito
對DAO層的單元測試進(jìn)行模擬后,得到的新的單元測試類如下 :
@RunWith(SpringRunner.class)
public class UserDaoTest {
@MockBean
private UserDao userDao;
private User userZhang = new User();
userZhang.setName("zhangSan");
userZhang.setAge(23);
@Before
public void setup() {
Mockito.when(userDao.findByName("zhangSan")).willReturn(userZhang);
Mockito.when(userDao.findByName("liSi")).willReturn(null);
}
@Test
public void testGetUser() {
Assert.assertEquals(userZhang, userDao.findByName("zhangSan"));
Assert.assertEquals(null, userDao.findByName("liSi"));
}
}
關(guān)于mockito
相關(guān),,請參考官網(wǎng):https://site./
后記
本文重在用代碼案例講解單元測試,,篇幅有限,先分享到這里,,如有不當(dāng)之處,,敬請諒解指出。
推薦閱讀