最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java 中的UnitTest 和 PowerMock

 更新時(shí)間:2020年06月03日 14:38:35   作者:程序員在深圳  
這篇文章主要介紹了Java中的 UnitTest 和 PowerMock,文中講解非常詳細(xì),對(duì)大家學(xué)習(xí)有很大的幫助,感興趣的朋友可以了解下

學(xué)習(xí)一門(mén)計(jì)算機(jī)語(yǔ)言,我覺(jué)得除了學(xué)習(xí)它的語(yǔ)法外,最重要的就是要學(xué)習(xí)怎么在這個(gè)語(yǔ)言環(huán)境下進(jìn)行單元測(cè)試,因?yàn)閱卧獪y(cè)試能幫你提早發(fā)現(xiàn)錯(cuò)誤;同時(shí)給你的程序加一道防護(hù)網(wǎng),防止你的修改破壞了原有的功能;單元測(cè)試還能指引你寫(xiě)出更好的代碼,畢竟不能被測(cè)試的代碼一定不是好代碼;除此之外,它還能增加你的自信,能勇敢的說(shuō)出「我的程序沒(méi)有bug」。

每個(gè)語(yǔ)言都有其常用的單元測(cè)試框架,本文主要介紹在 Java 中,我們?nèi)绾问褂?PowerMock,來(lái)解決我們?cè)趯?xiě)單元測(cè)試時(shí)遇到的問(wèn)題,從 Mock 這個(gè)詞可以看出,這類(lèi)問(wèn)題主要是解依賴(lài)問(wèn)題。

在寫(xiě)單元測(cè)試時(shí),為了讓測(cè)試工作更簡(jiǎn)單、減少外部的不確定性,我們一般都會(huì)把被測(cè)類(lèi)和其他依賴(lài)類(lèi)進(jìn)行隔離,不然你的類(lèi)依賴(lài)得越多,你需要做的準(zhǔn)備工作就越復(fù)雜,尤其是當(dāng)它依賴(lài)網(wǎng)絡(luò)或外部數(shù)據(jù)庫(kù)時(shí),會(huì)給測(cè)試帶來(lái)極大的不確定性,而我們的單測(cè)一定要滿足快速、可重復(fù)執(zhí)行的要求,所以隔離或解依賴(lài)是必不可少的步驟。

而 Java 中的 PowerMock 庫(kù)是一個(gè)非常強(qiáng)大的解依賴(lài)庫(kù),下面談到的 3 個(gè)特性,可以幫你解決絕大多數(shù)問(wèn)題:

1 通過(guò) PowerMock 注入依賴(lài)對(duì)象

2 利用 PowerMock 來(lái) mock static 函數(shù)

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

通過(guò) PowerMock 注入依賴(lài)對(duì)象

假設(shè)你有兩個(gè)類(lèi),MyService MyDao,MyService 依賴(lài)于 MyDao,且它們的定義如下

// MyDao.java
@Mapper
public interface MyDao {
  /**
   * 根據(jù)用戶(hù) id 查看他最近一次操作的時(shí)間
   */
  Date getLastOperationTime(long userId);
}

// MyService.java
@Service
public class MyService {
	@Autowired
	private MyDao myDao;
	
  public boolean operate(long userId, String operation) {
    Date lastTime = myDao.getLastOperationTime(userId);
    // ...
  }
}

這個(gè)服務(wù)提供一個(gè) operate 接口,用戶(hù)在調(diào)用該接口時(shí),會(huì)被限制一個(gè)操作頻次,所以系統(tǒng)會(huì)記錄每個(gè)用戶(hù)上次操作的時(shí)間,通過(guò) MyDao.getLastOperationTime(long userId) 接口獲取,現(xiàn)在我們要對(duì) MyService 類(lèi)的 operate 做單元測(cè)試,該怎么做?

你可能會(huì)想到使用 SpringBoot,它能自動(dòng)幫我們初始化 myDao 對(duì)象,但這樣做卻存在一些問(wèn)題:

1 SpringBoot 的啟動(dòng)速度很慢,這會(huì)延長(zhǎng)單元測(cè)試的時(shí)間

2 因?yàn)闀r(shí)間是一個(gè)不斷變化的量,也許這一次你構(gòu)造的時(shí)間滿足測(cè)試條件,但下一次運(yùn)行測(cè)試時(shí),可能就不滿足了。

由于以上原因,我們一般在做單元測(cè)試時(shí),不啟動(dòng) SpringBoot 上下文,而是采用 PowerMock 幫我們注入依賴(lài),對(duì)于上面的 case,我們的測(cè)試用例可以這樣寫(xiě):

// MyServiceTest.java
@RunWith(PowerMockRunner.class)
@PrepareForTest({MyService.class, MyDao.class})
public class MyServiceTest {
  @Test
  public void testOperate() throws IllegalAccessException {
    // 構(gòu)造一個(gè)和當(dāng)前調(diào)用時(shí)間永遠(yuǎn)只差 4 秒的返回值
  	Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.SECOND, -4);
    Date retTime = calendar.getTime();
    
    // spy 是對(duì)象的“部分 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);
    // 假設(shè)最小操作的間隔是 5 秒,否則返回 false
    Assert.assertFalse(myService.operate(1, "test operation"));
  }
}

從上面代碼中,我們首先構(gòu)造了一個(gè)返回時(shí)間 retTime,模擬操作間隔的時(shí)間為 4 秒,保證了每次運(yùn)行測(cè)試時(shí)該條件不會(huì)變化;然后我們用 spy 構(gòu)造一個(gè)待測(cè)試的 MyService 對(duì)象,spymock 的區(qū)別是,spy 只會(huì)部分模擬對(duì)象,即這里只修改掉 myService.myDao 成員,其他的保持不變。

然后我們定義了被 mock 的對(duì)象 MyDao md 的調(diào)用行為,當(dāng) md.getLastOperationTime 函數(shù)被調(diào)用時(shí),返回我們構(gòu)造的時(shí)間 retTime,此時(shí)測(cè)試環(huán)境就設(shè)置完畢了,這樣做之后,你就可以很容易的測(cè)試 operate 函數(shù)了。

利用 PowerMock 來(lái) mock static 函數(shù)

上文所說(shuō)的使用 PowerMock 進(jìn)行依賴(lài)注入,可以覆蓋測(cè)試中絕大多數(shù)的解依賴(lài)場(chǎng)景,而另一種常見(jiàn)的依賴(lài)是 static 函數(shù),例如我們自己寫(xiě)的一些 CommonUtil 工具類(lèi)中的函數(shù)。

還是使用上面的例子,假設(shè)我們要計(jì)算當(dāng)前時(shí)間和用戶(hù)上一次操作時(shí)間之間的間隔,并使用 public static long getTimeInterval(Date lastTime) 實(shí)現(xiàn)該功能,如下:

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

我們的 operator 函數(shù)修改如下

// 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 獲取上次操作的時(shí)間,再調(diào)用 CommonUtil.getTimeInterval 計(jì)算操作間隔,如果小于 5 秒,就返回 false,否則執(zhí)行操作,并返回 true。那么我的問(wèn)題是,如何解掉這里 static 函數(shù)的依賴(lài)呢?我們直接看測(cè)試代碼吧

// 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 對(duì)類(lèi) CommonUtil 進(jìn)行 mock,如果不這么做,這個(gè)類(lèi)中所有靜態(tài)函數(shù)的行為都會(huì)發(fā)生變化,這會(huì)給你的測(cè)試帶來(lái)麻煩。spy 下面的兩行代碼你應(yīng)該放在一起解讀,意為當(dāng)調(diào)用 CommonUtil.getTimeInterval 時(shí),返回 5;這種寫(xiě)法比較奇怪,但卻是 PowerMock 要求的。至此,你已經(jīng)掌握了 mock static 函數(shù)的技巧。

輸出參數(shù)(output parameter)怎么 mock

有些函數(shù)會(huì)通過(guò)修改參數(shù)所引用的對(duì)象作為輸出,例如下面的這個(gè)場(chǎng)景,假設(shè)我們的 operation 是一個(gè)長(zhǎng)時(shí)間執(zhí)行的任務(wù),我們需要不斷輪訓(xùn)該任務(wù)的狀態(tài),更新到內(nèi)存,并對(duì)外提供查詢(xún)接口,如下代碼:到內(nèi)存,并對(duì)外提供查詢(xún)接口,如下代碼:

// 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() 是一個(gè)輪詢(xún)?nèi)蝿?wù),它會(huì)不斷更新 operation 的狀態(tài),并在狀態(tài)達(dá)到 "success" 時(shí)停止,可以看到,updateStatus 就是我們所說(shuō)的函數(shù),雖然它沒(méi)有返回值,但它會(huì)修改參數(shù)所引用的對(duì)象,所以這種參數(shù)也被稱(chēng)作輸出參數(shù)。

現(xiàn)在我們要測(cè)試 run() 函數(shù)的行為,看它是否會(huì)在 "success" 狀態(tài)下退出,那么我們就需要 mock updateStatus 函數(shù),該怎么做?下面是它的測(cè)試代碼:

  @Test
  public void testUpdateStatus() throws InterruptedException {
    // 初始化被測(cè)對(duì)象
    MyTask myTask = PowerMockito.spy(new MyTask());
    myTask.setOperation(new MyTask.Operation());
    // 使用 doAnswer 來(lái) mock updateStatus 函數(shù)的行為
    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 來(lái) mock updateStatus 的行為,相當(dāng)于使用 answer 函數(shù)來(lái)替換原來(lái)的 updateStatus 函數(shù),在這里,我們將 operation 的狀態(tài)設(shè)置為了 "success",以期待 myTask.run() 函數(shù)返回 true。于是,我們又學(xué)會(huì)了如何 mock 具有輸出參數(shù)的函數(shù)了。

以上就是詳解Java 中的UnitTest 和 PowerMock的詳細(xì)內(nèi)容,更多關(guān)于Java UnitTest 和 PowerMock的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • @JsonDeserialize和@JsonSerialize注解的使用方式

    @JsonDeserialize和@JsonSerialize注解的使用方式

    這篇文章主要介紹了@JsonDeserialize和@JsonSerialize注解的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java字節(jié)緩沖流原理與用法詳解

    Java字節(jié)緩沖流原理與用法詳解

    這篇文章主要介紹了Java字節(jié)緩沖流原理與用法,結(jié)合實(shí)例形式總結(jié)分析了java字節(jié)流與緩沖區(qū)相關(guān)原理與操作技巧,需要的朋友可以參考下
    2019-09-09
  • Nginx啟用壓縮及開(kāi)啟gzip 壓縮的方法

    Nginx啟用壓縮及開(kāi)啟gzip 壓縮的方法

    這篇文章主要介紹了Nginx啟用壓縮及開(kāi)啟gzip 壓縮的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • Java獲取項(xiàng)目路徑的多種方式

    Java獲取項(xiàng)目路徑的多種方式

    這篇文章主要介紹了Java獲取項(xiàng)目路徑的多種方式,這時(shí)候就需要用java給我們提供的一些獲取相對(duì)路徑方法了,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-01-01
  • Idea2020.2創(chuàng)建JavaWeb項(xiàng)目(部署Tomcat)方法詳解

    Idea2020.2創(chuàng)建JavaWeb項(xiàng)目(部署Tomcat)方法詳解

    這篇文章主要介紹了Idea2020.2創(chuàng)建JavaWeb項(xiàng)目(部署Tomcat)方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • java:程序包c(diǎn)om.xxx.xxx不存在報(bào)錯(cuò)萬(wàn)能解決辦法

    java:程序包c(diǎn)om.xxx.xxx不存在報(bào)錯(cuò)萬(wàn)能解決辦法

    這篇文章主要給大家介紹了關(guān)于java:程序包c(diǎn)om.xxx.xxx不存在報(bào)錯(cuò)萬(wàn)能解決辦法,這個(gè)問(wèn)題曾逼瘋初學(xué)者的我,不過(guò)弄清楚原理后就很簡(jiǎn)單了,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 一篇文章帶你了解Java基礎(chǔ)-多態(tài)

    一篇文章帶你了解Java基礎(chǔ)-多態(tài)

    這篇文章主要介紹了Java 多態(tài)的深入理解的相關(guān)資料,子類(lèi)繼承父類(lèi)的特征和行為,使得子類(lèi)具有父類(lèi)的各種屬性和方法。或子類(lèi)從父類(lèi)繼承方法,使得子類(lèi)具有父類(lèi)相同的行為,需要的朋友可以參考下
    2021-08-08
  • Java編程IP地址和數(shù)字相互轉(zhuǎn)換代碼示例

    Java編程IP地址和數(shù)字相互轉(zhuǎn)換代碼示例

    這篇文章主要介紹了Java編程IP地址和數(shù)字相互轉(zhuǎn)換代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • 提高開(kāi)發(fā)效率Live?Templates使用技巧詳解

    提高開(kāi)發(fā)效率Live?Templates使用技巧詳解

    這篇文章主要為大家介紹了提高開(kāi)發(fā)效率Live?Templates使用技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 一文詳解如何在SpringMVC的視圖中渲染模型數(shù)據(jù)

    一文詳解如何在SpringMVC的視圖中渲染模型數(shù)據(jù)

    SpringMVC是一個(gè)基于Spring框架的Web框架,它提供了一種方便的方式來(lái)處理 HTTP 請(qǐng)求和響應(yīng),在SpringMVC中,視圖是用來(lái)渲染模型數(shù)據(jù)的組件,它們負(fù)責(zé)將模型數(shù)據(jù)轉(zhuǎn)換為HTML、JSON、XML等格式的響應(yīng),在本文中,我們將討論如何在SpringMVC中的視圖中渲染模型數(shù)據(jù)
    2023-07-07

最新評(píng)論

德化县| 德格县| 齐齐哈尔市| 廉江市| 资兴市| 绵竹市| 天门市| 策勒县| 宜丰县| 辰溪县| 平江县| 虞城县| 库伦旗| 固安县| 阳春市| 略阳县| 邛崃市| 德庆县| 海原县| 新竹市| 扎赉特旗| 固始县| 江永县| 天津市| 加查县| 淄博市| 安远县| 射阳县| 抚宁县| 六盘水市| 新和县| 香港| 公安县| 沾化县| 蛟河市| 永济市| 晋宁县| 正蓝旗| 东台市| 乳山市| 当雄县|