电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳解Java 中的UnitTest 和 PowerMock

瀏覽:58日期:2022-08-31 16:39:44

學習一門計算機語言,我覺得除了學習它的語法外,最重要的就是要學習怎么在這個語言環境下進行單元測試,因為單元測試能幫你提早發現錯誤;同時給你的程序加一道防護網,防止你的修改破壞了原有的功能;單元測試還能指引你寫出更好的代碼,畢竟不能被測試的代碼一定不是好代碼;除此之外,它還能增加你的自信,能勇敢的說出「我的程序沒有bug」。

每個語言都有其常用的單元測試框架,本文主要介紹在 Java 中,我們如何使用 PowerMock,來解決我們在寫單元測試時遇到的問題,從 Mock 這個詞可以看出,這類問題主要是解依賴問題。

在寫單元測試時,為了讓測試工作更簡單、減少外部的不確定性,我們一般都會把被測類和其他依賴類進行隔離,不然你的類依賴得越多,你需要做的準備工作就越復雜,尤其是當它依賴網絡或外部數據庫時,會給測試帶來極大的不確定性,而我們的單測一定要滿足快速、可重復執行的要求,所以隔離或解依賴是必不可少的步驟。

而 Java 中的 PowerMock 庫是一個非常強大的解依賴庫,下面談到的 3 個特性,可以幫你解決絕大多數問題:

1 通過 PowerMock 注入依賴對象

2 利用 PowerMock 來 mock static 函數

3 輸出參數(output parameter)怎么 mock

通過 PowerMock 注入依賴對象

假設你有兩個類,MyService 和 MyDao,MyService 依賴于 MyDao,且它們的定義如下

// MyDao.java@Mapperpublic interface MyDao { /** * 根據用戶 id 查看他最近一次操作的時間 */ Date getLastOperationTime(long userId);}// MyService.java@Servicepublic class MyService {@Autowiredprivate MyDao myDao; public boolean operate(long userId, String operation) { Date lastTime = myDao.getLastOperationTime(userId); // ... }}

這個服務提供一個 operate 接口,用戶在調用該接口時,會被限制一個操作頻次,所以系統會記錄每個用戶上次操作的時間,通過 MyDao.getLastOperationTime(long userId) 接口獲取,現在我們要對 MyService 類的 operate 做單元測試,該怎么做?

你可能會想到使用 SpringBoot,它能自動幫我們初始化 myDao 對象,但這樣做卻存在一些問題:

1 SpringBoot 的啟動速度很慢,這會延長單元測試的時間

2 因為時間是一個不斷變化的量,也許這一次你構造的時間滿足測試條件,但下一次運行測試時,可能就不滿足了。

由于以上原因,我們一般在做單元測試時,不啟動 SpringBoot 上下文,而是采用 PowerMock 幫我們注入依賴,對于上面的 case,我們的測試用例可以這樣寫:

// MyServiceTest.java@RunWith(PowerMockRunner.class)@PrepareForTest({MyService.class, MyDao.class})public class MyServiceTest { @Test public void testOperate() throws IllegalAccessException { // 構造一個和當前調用時間永遠只差 4 秒的返回值 Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.SECOND, -4); Date retTime = calendar.getTime();// spy 是對象的“部分 mock” MyService myService = PowerMockito.spy(new MyService()); MyDao md = PowerMockito.mock(MyDao.class); PowerMockito.when(md.getLastOperationTime(Mockito.any(long.class))).thenReturn(retTime); // 替換 myDao 成員 MemberModifier.field(MyService.class, 'myDao').set(myService, md); // 假設最小操作的間隔是 5 秒,否則返回 false Assert.assertFalse(myService.operate(1, 'test operation')); }}

從上面代碼中,我們首先構造了一個返回時間 retTime,模擬操作間隔的時間為 4 秒,保證了每次運行測試時該條件不會變化;然后我們用 spy 構造一個待測試的 MyService 對象,spy 和 mock 的區別是,spy 只會部分模擬對象,即這里只修改掉 myService.myDao 成員,其他的保持不變。

然后我們定義了被 mock 的對象 MyDao md 的調用行為,當 md.getLastOperationTime 函數被調用時,返回我們構造的時間 retTime,此時測試環境就設置完畢了,這樣做之后,你就可以很容易的測試 operate 函數了。

利用 PowerMock 來 mock static 函數

上文所說的使用 PowerMock 進行依賴注入,可以覆蓋測試中絕大多數的解依賴場景,而另一種常見的依賴是 static 函數,例如我們自己寫的一些 CommonUtil 工具類中的函數。

還是使用上面的例子,假設我們要計算當前時間和用戶上一次操作時間之間的間隔,并使用 public static long getTimeInterval(Date lastTime) 實現該功能,如下:

// CommonUtil.javaclass CommonUtil { public static long getTimeInterval(Date lastTime) { long duration = Duration.between(lastTime.toInstant(),new Date().toInstant()).getSeconds(); return duration; }}

我們的 operator 函數修改如下

// MyService.java// ... public boolean operate(long userId, String operation) { Date lastTime = myDao.getLastOperationTime(userId); long duration = CommonUtil.getTimeInterval(lastTime); if (duration >= 5) { System.out.println('user: ' + userId + ' ' + operation); return true; } else { return false; } }// ...

這里先從 myDao 獲取上次操作的時間,再調用 CommonUtil.getTimeInterval 計算操作間隔,如果小于 5 秒,就返回 false,否則執行操作,并返回 true。那么我的問題是,如何解掉這里 static 函數的依賴呢?我們直接看測試代碼吧

// MyServiceTest.java@PrepareForTest({MyService.class, MyDao.class, CommonUtil.class})public class MyServiceTest {// ... @Test public void testOperateWithStatic() throws IllegalAccessException { // ... PowerMockito.spy(CommonUtil.class); PowerMockito.doReturn(5L).when(CommonUtil.class); CommonUtil.getTimeInterval(Mockito.anyObject()); // ... }}

首先在注解 @PrepareForTest 中增加 CommonUtil.class,依然使用 spy 對類 CommonUtil 進行 mock,如果不這么做,這個類中所有靜態函數的行為都會發生變化,這會給你的測試帶來麻煩。spy 下面的兩行代碼你應該放在一起解讀,意為當調用 CommonUtil.getTimeInterval 時,返回 5;這種寫法比較奇怪,但卻是 PowerMock 要求的。至此,你已經掌握了 mock static 函數的技巧。

輸出參數(output parameter)怎么 mock

有些函數會通過修改參數所引用的對象作為輸出,例如下面的這個場景,假設我們的 operation 是一個長時間執行的任務,我們需要不斷輪訓該任務的狀態,更新到內存,并對外提供查詢接口,如下代碼:到內存,并對外提供查詢接口,如下代碼:

// MyTask.java// ... public boolean run() throws InterruptedException { while (true) { updateStatus(operation); if (operation.getStatus().equals('success')) {return true; } else {Thread.sleep(1000); } } } public void updateStatus(Operation operation) { String status = myDao.getStatus(operation.getOperationId()); operation.setStatus(status); }// ...

上面的代碼中,run() 是一個輪詢任務,它會不斷更新 operation 的狀態,并在狀態達到 'success' 時停止,可以看到,updateStatus 就是我們所說的函數,雖然它沒有返回值,但它會修改參數所引用的對象,所以這種參數也被稱作輸出參數。

現在我們要測試 run() 函數的行為,看它是否會在 'success' 狀態下退出,那么我們就需要 mock updateStatus 函數,該怎么做?下面是它的測試代碼:

@Test public void testUpdateStatus() throws InterruptedException { // 初始化被測對象 MyTask myTask = PowerMockito.spy(new MyTask()); myTask.setOperation(new MyTask.Operation()); // 使用 doAnswer 來 mock updateStatus 函數的行為 PowerMockito.doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable {Object[] args = invocation.getArguments();MyTask.Operation operation = (MyTask.Operation)args[0];operation.setStatus('success');return null; } }).when(myTask).updateStatus(Mockito.any(MyTask.Operation.class)); Assert.assertEquals(true, myTask.run()); }

上面的代碼中,我們使用 doAnswer 來 mock updateStatus 的行為,相當于使用 answer 函數來替換原來的 updateStatus 函數,在這里,我們將 operation 的狀態設置為了 'success',以期待 myTask.run() 函數返回 true。于是,我們又學會了如何 mock 具有輸出參數的函數了。

以上就是詳解Java 中的UnitTest 和 PowerMock的詳細內容,更多關于Java UnitTest 和 PowerMock的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
主站蜘蛛池模板: 郑州水质检测中心_井水检测_河南废气检测_河南中环嘉创检测 | 短信通106短信接口验证码接口群发平台_国际短信接口验证码接口群发平台-速度网络有限公司 | 微型驱动系统解决方案-深圳市兆威机电股份有限公司 | 日本细胞免疫疗法_肿瘤免疫治疗_NK细胞疗法 - 免疫密码 | 能量回馈_制动单元_电梯节能_能耗制动_深圳市合兴加能科技有限公司 | 气动隔膜阀_气动隔膜阀厂家_卫生级隔膜阀价格_浙江浙控阀门有限公司 | 过跨车_过跨电瓶车_过跨转运车_横移电动平车_厂区转运车_无轨转运车 | 小程序开发公司-小程序制作-微信小程序开发-小程序定制-咏熠软件 | 北京翻译公司-专业合同翻译-医学标书翻译收费标准-慕迪灵 | 工业车间焊接-整体|集中除尘设备-激光|等离子切割机配套除尘-粉尘烟尘净化治理厂家-山东美蓝环保科技有限公司 | 选矿设备,选矿生产线,选矿工艺,选矿技术-昆明昆重矿山机械 | 披萨石_披萨盘_电器家电隔热绵加工定制_佛山市南海区西樵南方综合保温材料厂 | 颗粒机,颗粒机组,木屑颗粒机-济南劲能机械有限公司 | led全彩屏-室内|学校|展厅|p3|户外|会议室|圆柱|p2.5LED显示屏-LED显示屏价格-LED互动地砖屏_蕙宇屏科技 | 哈希余氯测定仪,分光光度计,ph在线监测仪,浊度测定仪,试剂-上海京灿精密机械有限公司 | 便携式XPDM露点仪-在线式防爆露点仪-增强型烟气分析仪-约克仪器 冰雕-冰雪世界-大型冰雕展制作公司-赛北冰雕官网 | 伊卡洛斯软装首页-电动窗帘,别墅窗帘,定制窗帘,江浙沪1000+别墅窗帘案例 | 创客匠人-让IP变现不走弯路| 冷油器,取样冷却器,热力除氧器-连云港振辉机械设备有限公司 | 整合营销推广|营销网络推广公司|石家庄网站优化推广公司|智营销 好物生环保网、环保论坛 - 环保人的学习交流平台 | 三价铬_环保铬_环保电镀_东莞共盈新材料贸易有限公司 | 不锈钢监控杆_监控立杆厂家-廊坊耀星光电科技有限公司 | 不锈钢反应釜,不锈钢反应釜厂家-价格-威海鑫泰化工机械有限公司 不干胶标签-不干胶贴纸-不干胶标签定制-不干胶标签印刷厂-弗雷曼纸业(苏州)有限公司 | _网名词典_网名大全_qq网名_情侣网名_个性网名 | 杭州货架订做_组合货架公司_货位式货架_贯通式_重型仓储_工厂货架_货架销售厂家_杭州永诚货架有限公司 | 工业用品一站式采购平台|南创工品汇-官网|广州南创 | 激光内雕_led玻璃_发光玻璃_内雕玻璃_导光玻璃-石家庄明晨三维科技有限公司 激光内雕-内雕玻璃-发光玻璃 | 雷冲击高压发生器-水内冷直流高压发生器-串联谐振分压器-武汉特高压电力科技有限公司 | 天津热油泵_管道泵_天津高温热油泵-天津市金丰泰机械泵业有限公司【官方网站】 | 学考网学历中心| 高防护蠕动泵-多通道灌装系统-高防护蠕动泵-www.bjhuiyufluid.com慧宇伟业(北京)流体设备有限公司 | 浙江寺庙设计-杭州寺院设计-宁波寺庙规划_汉匠 | 搅拌磨|搅拌球磨机|循环磨|循环球磨机-无锡市少宏粉体科技有限公司 | 空气能暖气片,暖气片厂家,山东暖气片,临沂暖气片-临沂永超暖通设备有限公司 | 选矿设备-新型重选设备-金属矿尾矿重选-青州冠诚重工机械有限公司 | 广东护栏厂家-广州护栏网厂家-广东省安麦斯交通设施有限公司 | 北京三友信电子科技有限公司-ETC高速自动栏杆机|ETC机柜|激光车辆轮廓测量仪|嵌入式车道控制器 | 汽车整车综合环境舱_军标砂尘_盐雾试验室试验箱-无锡苏南试验设备有限公司 | 刑事律师_深圳著名刑事辩护律师_王平聚【清华博士|刑法教授】 | 工业CT-无锡璟能智能仪器有限公司 | 合肥升降机-合肥升降货梯-安徽升降平台「厂家直销」-安徽鼎升自动化科技有限公司 |