java单元测试之mock篇
java單元測(cè)試之mock篇
- 一、什么是mock?
- 二、為什么要進(jìn)行mock?
- 三、IDEA中使用Mock
- 3.1、引入mock所需依賴(lài)
- 3.1、IDEA單元測(cè)試必備快捷鍵
- 3.2、Mock測(cè)試相關(guān)注解
- @Mock注解
- @InjectMocks注解
- 調(diào)用PowerMockito.spy()方法
- Mock使用方式或者技巧
- 靜態(tài)方法mockStatic
- @PrepareForTest的使用場(chǎng)景
- 阻止代碼初始化,包括static{}靜態(tài)代碼塊或者static變量的初始化
- 3.3、問(wèn)題
一、什么是mock?
Mock 測(cè)試就是在測(cè)試過(guò)程中,對(duì)于某些不容易構(gòu)造(如 HttpServletRequest 必須在Servlet 容器中才能構(gòu)造出來(lái))或者不容易獲取的比較復(fù)雜的對(duì)象(如 JDBC 中的ResultSet 對(duì)象),用一個(gè)虛擬的對(duì)象(Mock 對(duì)象)來(lái)創(chuàng)建以便測(cè)試的測(cè)試方法。
二、為什么要進(jìn)行mock?
Mock是為了解決不同的單元之間由于耦合而難于開(kāi)發(fā)、測(cè)試的問(wèn)題。所以,Mock既能出現(xiàn)在單元測(cè)試中,也會(huì)出現(xiàn)在集成測(cè)試、系統(tǒng)測(cè)試過(guò)程中。Mock 最大的功能是幫你把單元測(cè)試的耦合分解開(kāi),如果你的代碼對(duì)另一個(gè)類(lèi)或者接口有依賴(lài),它能夠幫你模擬這些依賴(lài),并幫你驗(yàn)證所調(diào)用的依賴(lài)的行為。
比如一段代碼有這樣的依賴(lài):
當(dāng)我們需要測(cè)試A類(lèi)的時(shí)候,如果沒(méi)有 Mock,則我們需要把整個(gè)依賴(lài)樹(shù)都構(gòu)建出來(lái),而使用 Mock 的話(huà)就可以將結(jié)構(gòu)分解開(kāi),像下面這樣:
三、IDEA中使用Mock
3.1、引入mock所需依賴(lài)
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.9.0</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-core</artifactId><version>2.0.9</version></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.9</version></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.9</version><scope>test</scope></dependency>3.1、IDEA單元測(cè)試必備快捷鍵
新建UT——Ctrl + Shift + T
雙擊所要進(jìn)行單元測(cè)試的類(lèi),點(diǎn)擊Ctrl + Shift + T,新建或者跳轉(zhuǎn)到已創(chuàng)建的UT,這里我們點(diǎn)擊Create New Test…新建UT,選擇JUnit4,選擇好需要測(cè)試的方法后點(diǎn)擊OK。
3.2、Mock測(cè)試相關(guān)注解
@Mock注解
@RunWith(PowerMockRunner.class) public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());} }從上面代碼可以看出,被@Mock修飾后的變量announcementMapper則會(huì)產(chǎn)生一個(gè)AnnouncementMapper的mock類(lèi),不用@mock注解用mock方法來(lái)產(chǎn)生一個(gè)announcementMapper是一樣的效果。
@InjectMocks注解
@RunWith(PowerMockRunner.class) public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());} }@InjectMocks注解修飾的類(lèi)不能修飾抽象或接口類(lèi),被它修飾后,其被mock后的成員變量會(huì)注入
到@InjectMocks的類(lèi)中。
比如上面的announcementMapper則會(huì)被注入到announcementManagerImpl中,當(dāng)調(diào)用announcementManagerImpl時(shí),原代碼里的announcementMapper.selectCountByParams(queryWrapper)方法則會(huì)執(zhí)行mock方法
調(diào)用PowerMockito.spy()方法
通過(guò)spy,Whilebox.setInternelState方法設(shè)置內(nèi)部依賴(lài)
public class AnnouncementManagerImplTest {AnnouncementMapper announcementMapper;AnnouncementRepository announcementRepository;AnnouncementManagerImpl announcementManagerImpl;@Beforepublic void before() {MockitoAnnotations.openMocks(this);announcementManagerImpl = PowerMockito.spy(new AnnouncementManagerImpl());announcementMapper = PowerMockito.mock(AnnouncementMapper.class);Whitebox.setInternalState(announcementManagerImpl,"announcementMapper",announcementMapper);List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());} }Mock使用方式或者技巧
在測(cè)試類(lèi)上加注解@RunWith(PowerMockRunner.class)
等同于在@Before修飾的before方法中添加MockitoAnnotations.openMocks(this);
@Spy等同于PowerMockito.spy()方法
@Mock等同于PowerMockito.mock()方法
但是調(diào)用PowerMockito的方法時(shí),必須要調(diào)用Whitebox.setInternalState()設(shè)置內(nèi)部依賴(lài)
靜態(tài)方法mockStatic
@RunWith(PowerMockRunner.class) @PrepareForTest(IdUtils.class) public class AnnouncementManagerImplTest {@MockAnnouncementMapper announcementMapper;@MockAnnouncementRepository announcementRepository;@InjectMocks@SpyAnnouncementManagerImpl announcementManagerImpl = new AnnouncementManagerImpl();@Beforepublic void before() {PowerMockito.mockStatic(IdUtils.class);List<Announcement> res = new ArrayList<>();Announcement announcement = new Announcement();announcement.setId("dsfsdf");announcement.setAnnouncementContent("dsfbuiashf");res.add(announcement);PowerMockito.when(IdUtils.randomUUID()).thenReturn("sahduiashdi");PowerMockito.when(announcementMapper.queryByParams(Mockito.any(QueryWrapper.class), Mockito.any(Page.class))).thenReturn(res);PowerMockito.when(announcementMapper.selectCountByParams(Mockito.any(QueryWrapper.class))).thenReturn(1l);}@Testpublic void save() {}@Testpublic void findByPage() {QueryParams<AnnouncementDTO> params = new QueryParams<>();params.setPage(new Page(1,10));List<SortInfo> sort = new ArrayList<>();params.setSortInfo(sort);List<InputField> fields = new ArrayList<>();params.setParams(fields);IPage<Announcement> page = announcementManagerImpl.findByPage(params);Assert.assertNotNull(page.getTotal());Assert.assertNotNull(page.getRecords());} }當(dāng)需要mock靜態(tài)方法的時(shí)候,必須要加注解@PrepareForTest和@RunWith。注解@PrepareForTest里寫(xiě)的類(lèi)是靜態(tài)方法所在的類(lèi)。
添加完注解再使用PowerMockito.mockStatic()方法,mock靜態(tài)類(lèi)。
@PrepareForTest的使用場(chǎng)景
當(dāng)使用PowerMockito.whenNew方法時(shí),必須加注解@PrepareForTest和@RunWith。注解@PrepareForTest里寫(xiě)的類(lèi)是需要mock的new對(duì)象代碼所在的類(lèi)。
當(dāng)需要mock final方法的時(shí)候,必須加注解@PrepareForTest和@RunWith。注解@PrepareForTest里寫(xiě)的類(lèi)是final方法所在的類(lèi)。
當(dāng)需要mock靜態(tài)方法的時(shí)候,必須加注解@PrepareForTest和@RunWith。注解@PrepareForTest里寫(xiě)的類(lèi)是靜態(tài)方法所在的類(lèi)。
當(dāng)需要mock私有方法的時(shí)候, 只是需要加注解@PrepareForTest,注解里寫(xiě)的類(lèi)是私有方法所在的類(lèi)
當(dāng)需要mock系統(tǒng)類(lèi)的靜態(tài)方法的時(shí)候,必須加注解@PrepareForTest和@RunWith。注解里寫(xiě)的類(lèi)是需要調(diào)用系統(tǒng)方法所在的類(lèi)
阻止代碼初始化,包括static{}靜態(tài)代碼塊或者static變量的初始化
@SuppressStaticInitializationFor("cn.com.jc.utils.IdUtils")3.3、問(wèn)題
1、使用@InjectMocks修飾接口類(lèi)報(bào)錯(cuò)
解決方法:
①收到new一個(gè)實(shí)現(xiàn)類(lèi)的對(duì)象
②可以在@Before修飾的before()方法中添加spy方法注入該實(shí)現(xiàn)類(lèi)對(duì)象
@InjectMocksAnnouncementManagerImpl announcementManager;@Beforepublic void before() {announcementManager = PowerMockito.spy(new AnnouncementManagerImpl());}總結(jié)
以上是生活随笔為你收集整理的java单元测试之mock篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2022nlp视频教程大全 NLP自然语
- 下一篇: axure动态面板